使用Aeson(Haskell)解码枚举选择

时间:2013-10-25 06:33:09

标签: json haskell aeson

我在确定如何为Enum类型定义FromJSON实例时遇到了一些麻烦,Enum类型定义了两种其他类型之间的选择。我的预感是我对the< *>和(。:)运算符没有足够的理解,以及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和deriveJSON为我定义这个,但即使这不会导致问题,我也很好奇如何解决这个问题。

编辑:deriveJSON效果很好。不过,我仍然很好奇如何手工制作。

1 个答案:

答案 0 :(得分: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 ""