我正在使用Data.Aeson将JSON解析为我的自定义类型。
我尝试在我的Vector Value
实例中模式匹配Array
(FromJSON
),但不知道我该怎么做。 JSON value
密钥的值为String
,String
列表或String
列表列表。
instance FromJSON Foo where
parseJSON (Object o) =
case lookup "value" o of
Just (String s) -> pure $ SimpleText s
Just foo@(Array (String s)) -> pure $ ListOfText $ V.toList <$> V.mapM (parseJSON :: Value -> Parser Text) foo
Just foo@(Array (Array (String s))) -> pure $ ListOfListOfText $ V.toList <$> V.mapM (parseJSON :: Value -> Parser Text) $ V.toList <$> V.mapM (parseJSON :: Value -> [Parser Value]) foo
带
data Foo = SimpleText Text
| ListOfText [Text]
| ListOfListOfText [[Text]]
deriving (Show, Eq, Ord)
我可以在Array上使用模式匹配来处理这种情况吗?或者我应该手动检查每个Value
的类型?怎么做?
答案 0 :(得分:3)
不,你不能按照你在这里尝试的方式进行模式匹配。 JSON数组可以包含不同类型的值,并且您不能对列表中的所有值进行模式匹配,就像它们在一个值中一样。
有几种方法可以解决您的实际问题。有一种简单方法,并且有一种显式方式可以为您提供更好的错误消息。
简单的方法是使用FromJSON
和Text
已存在[a]
个实例的事实。因此,您可以使用Alternative
运算符来编写您的实例,如下所示:
instance FromJSON Foo where
parseJSON v = (SimpleText <$> parseJSON v)
<|> (ListOfText <$> parseJSON v)
<|> (ListOfListOfText <$> parseJSON v)
这里的诀窍是Aeson首先会尝试解析Text
值,然后如果失败则会尝试[Text]
,如果再次失败则会尝试[[Text]]
。< / p>
此解决方案的问题在于,如果您的JSON格式错误,则错误消息可能没有意义。例如,如果你给它一个顶级Null值,你的错误将是它期望[[Text]]
,因为你总是得到链中最后一个值的错误。
要获得更好的错误消息,您必须更加关注您期望的值。如果结果是空数组,如果它是ListOfText
还是ListOfListOfText
怎么办?由于我们无法直接在Vector
上进行模式匹配,因此我们可以将其转换为列表和模式匹配:
instance FromJSON Foo where
parseJSON v = case v of
-- If its a string, we return the string as a SimpleText
(String s) -> return $ SimpleText s
-- If its an array, we turn the vector to a list so we can pattern match on it
(Array a) -> case V.toList a of
-- If its a empty list, we return a empty ListOfText
[] -> return $ ListOfText []
-- If the first value is a string, we put it as the first element of our ListOfTexts and try to parse the rest.
(String s: xs) -> ListOfText . (s:) <$> mapM parseJSON xs
-- If the first value is an array, we try to parse it as [Text], then parse the rest.
(Array a: xa) -> ListOfListOfText <$> ((:) <$> parseJSON (Array a) <*> mapM parseJSON xa)
-- If the first value is neither a string or array we return a error message.
_ -> fail "Expected an Array or an Array of Arrays."
-- If the top level value is not a string or array we return a error message.
_ -> fail "Expected a String or Array"