【问题标题】:Decoding Enum choices using Aeson (Haskell)使用 Aeson (Haskell) 解码枚举选择
【发布时间】:2013-10-25 06:33:08
【问题描述】:

我在弄清楚如何为定义在其他两种类型之间选择的 Enum 类型定义 FromJSON 实例时遇到了一些麻烦。我的预感是我对 、 和 (.:) 运算符以及 Aeson Object 类型的工作原理没有充分的了解,但我无法解析编译器错误然而。 (谢天谢地,ToJSON 实例很简单。)

给定两个子数据类型,我可以这样定义实例:

data ChoiceSelection =
   ChoiceSelection
      { csValue :: Type1  -- Type2 here also works fine
      } deriving (Show,Typeable)

data Type1 =
   Type1
      { t1Value :: Int
      } deriving (Show,Typeable)

data Type2 =
   Type2
      { t2Value :: Bool
      } deriving (Show,Typeable)

instance FromJSON ChoiceSelection where
   parseJSON (Object x) =   ChoiceSelection
                        <$> (x .: "csValue")
   parseJSON _          = mzero

instance FromJSON Type1 where
   parseJSON (Object x) =   Type1
                        <$> (x .: "t1Value")
   parseJSON _          = mzero

instance FromJSON Type2 where
   parseJSON (Object x) =   Type2
                        <$> (x .: "t2Value")
   parseJSON _          = mzero

instance ToJSON ChoiceSelection where
   toJSON (ChoiceSelection value) =
      object [ "csValue" .= value
             ]

instance ToJSON Type1 where
   toJSON (Type1 value) =
      object [ "t1Value" .= value
             ]

instance ToJSON Type2 where
   toJSON (Type2 value) =
      object [ "t2Value" .= value
             ]

这很好用,但我无法为 ExampleChoice 定义 FromJSON 的实例:

data ExampleChoice = Choice1 Type1
                   | Choice2 Type2
                   deriving (Show,Typeable)

data ChoiceSelection =
   ChoiceSelection
      { csValue :: ExampleChoice
      } deriving (Show,Typeable)

instance FromJSON ExampleChoice where
   parseJSON (Object x) = -- ???
   parseJSON _          = mzero

instance ToJSON ExampleChoice where
   toJSON (Choice1 t@(Type1 _)) = toJSON t
   toJSON (Choice2 t@(Type2 _)) = toJSON t

我曾想过尝试将其定义为 msum,如下所示:

instance FromJSON ExampleChoice where
   parseJSON (Object x) =
      msum [ -- Some attempt at parsing Type1
           , -- Some attempt at parsing Type2
           , mzero
           ]
   parseJSON _          = mzero

但是,我还没有弄清楚那个解析。

我还没有尝试使用 TemplateHaskell 和 derivedJSON 为我定义这个,但即使这不会导致问题,我很好奇如何解决这个问题。

编辑:deriveJSON 效果很好。不过,我仍然很好奇如何手动构建它。

【问题讨论】:

    标签: json haskell aeson


    【解决方案1】:

    您需要更改您的 ToJSON 实例,以便您可以识别要使用的数据构造函数(我没有测试过代码,但我希望这能给您带来想法):

    import qualified Data.HashMap.Strict as H
    
    instance ToJSON ExampleChoice where
       toJSON (Choice1 t@(Type1 _)) = object ["Choice1" .= t]
       toJSON (Choice2 t@(Type2 _)) = object ["Choice2" .= t]
    
    
    instance FromJSON ExampleChoice
        parseJSON (Object (H.toList -> [(key, value)]))
            | key == "Choice1"  = Choice1 <$> parseJSON value
            | key == "Choice2" = Choice2 <$> parseJSON value
        parseJSON _        = fail ""
    

    【讨论】:

    • 我最终确实得到了类似的工作,使用 msum 方法。我意识到我的问题是误解了 运算符,我现在更好地理解为 fmap。我无法在此评论中获得格式,但在一行中它看起来像这样: parseJSON (Object x) = msum [ Choice1 parseJSON x, Choice2 parseJSON x ]
    • 另外,我注意到自动派生使 JSON 将 ChoiceN 元素包装在一个 ExampleChoice 元素中,但是这个解决方案很好地“隐藏”了 ExampleChoice 功能,使其不会出现在 JSON 中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多