我正在使用一个返回 JSON 响应的外部 API。其中一个响应是对象数组,这些对象由其中的字段值标识。我在理解如何使用 Aeson 解析这种 JSON 响应时遇到了一些麻烦。
这是我的问题的简化版本:
newtype Content = Content { content :: [Media] } deriving (Generic)
instance FromJSON Content
data Media =
Video { objectClass :: Text
, title :: Text } |
AudioBook { objectClass :: Text
, title :: Text }
在 API 文档中,据说可以通过字段 objectClass 来识别对象,该字段对于我们的 Video 对象具有值“video”,对于我们的 AudioBook 等具有值“audiobook”。示例 JSON:
[{objectClass: "video", title: "Some title"}
,{objectClass: "audiobook", title: "Other title"}]
问题是如何使用 Aeson 处理这种类型的 JSON?
instance FromJSON Media where
parseJSON (Object x) = ???
最佳答案
你基本上需要一个函数Text -> Text -> Media
:
toMedia :: Text -> Text -> Media
toMedia "video" = Video "video"
toMedia "audiobook" = AudioBook "audiobook"
FromJSON
实例现在非常简单(使用 <$>
和 <*>
中的 Control.Applicative
):instance FromJSON Media where
parseJSON (Object x) = toMedia <$> x .: "objectClass" <*> x .: "title"
但是,此时您是多余的:
objectClass
Video
中的字段或 Audio
不会为您提供比实际类型更多的信息,因此您可以将其删除:data Media = Video { title :: Text }
| AudioBook { title :: Text }
toMedia :: Text -> Text -> Media
toMedia "video" = Video
toMedia "audiobook" = AudioBook
另请注意
toMedia
是局部的。您可能想捕获无效的 "objectClass"
值(value)观:instance FromJSON Media where
parseJSON (Object x) =
do oc <- x .: "objectClass"
case oc of
String "video" -> Video <$> x .: "title"
String "audiobook" -> AudioBook <$> x .: "title"
_ -> empty
{- an alternative using a proper toMedia
toMedia :: Alternative f => Text -> f (Text -> Media)
toMedia "video" = pure Video
toMedia "audiobook" = pure AudioBook
toMedia _ = empty
instance FromJSON Media where
parseJSON (Object x) = (x .: "objectClass" >>= toMedia) <*> x .: "title"
-}
最后但同样重要的是,请记住 valid JSON使用字符串作为名称。
关于json - Haskell::Aeson::根据字段值解析ADT,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25500871/