如何为具有多个构造函数的数据类型编写ToJSON / FromJSON实例?

时间:2012-04-26 18:41:59

标签: haskell

我在ToJSONFromJSON看到的每个示例都是针对具有单个构造函数的数据类型,如下所示:

data RewindConfig = RConfig JobID Phase
                      deriving Show

instance FromJSON RewindConfig where
  parseJSON (Object o) = RConfig
    <$> o .: "JobID"
    <*> o .: "Phase"
  parseJSON _ = fail "invalid RewindConfig"

我想我会看看Aeson如何为具有多个构造函数的类型创建实例,例如Either

instance (FromJSON a, FromJSON b) => FromJSON (Either a b) where
   parseJSON (Object (H.toList -> [(key, value)]))
        | key == left  = Left  <$> parseJSON value
        | key == right = Right <$> parseJSON value
   parseJSON _        = fail ""

parseJSON中的模式匹配让我困惑,我不明白(H.toList -> [(key, value)])发生了什么。

我想要创建实例的数据类型如下所示:

data Foo = Bar String
         | Baz String
         | Bin String

我确实想要做一些我知道如何实施的事情

data Foo = (Maybe Bar) (Maybe Baz) (Maybe Bin)

但这似乎并不令人满意。有人可以通过解释Either实例的内容来帮助我,也许可以给我一些关于Foo的To / From实例的指导吗?

更新:我认为Aeson为Maybe实现的实例更加清晰,并告诉我需要了解的内容。不过,我想知道Either发生了什么。

3 个答案:

答案 0 :(得分:4)

模式(Object (H.toList -> [(key, value)]))称为view pattern。您可以将其读作以下内容:

parseJSon (Object o) = case H.toList o of
    [(key, value)]
        | key == left  -> Left  <$> parseJSON value
        | key == right -> Right <$> parseJSON value

它实际上略有不同,因为上面的内容总是会在传递Object o时提交模式Object,而视图模式只会在“匹配Object o模式时提交“和”H.toList o匹配[(key, value)]模式“条件成立,但对于此示例无关紧要。

答案 1 :(得分:2)

json包中包含您可能想要采用的数据类型的编码。 如果你只是派生Data,你可以使用它。它不是很快,但很容易使用。

答案 2 :(得分:1)

假设每个数据类型都有一个不同的键,另一种方法可以使用镜头 - 我喜欢它,因为它简洁且可读。例如,如果你有一个包含A,B和C的包装器,它们都有FromJSON实例:

import Data.Aeson
import Data.Maybe
import Data.Aeson.Lens
import Control.Lens

data Wrap = WrapA A | WrapB B | WrapC C

instance FromJSON Wrap where
    parseJSON json
        | isJust (json ^? key "A's unique key") = WrapA <$> parseJSON json
        | isJust (json ^? key "B's unique key") = WrapB <$> parseJSON json
        | isJust (json ^? key "C's unique key") = WrapC <$> parseJSON json
        | otherwise = fail "Bad message"