我目前正在努力使用aeson
库解析一些JSON数据。当缺少该属性的数据时,有许多属性具有值false
。因此,如果属性的值通常是整数数组,并且恰好没有该属性的数据,则值为null
,而不是提供空数组或false
。 (这种数据结构的方式不是我这样做的,所以我必须以某种方式使用它。)
理想情况下,我希望在值为布尔值的情况下最终得到一个空列表。我在下面创建了一个小测试用例进行演示。因为我的Group
数据构造函数需要一个列表,所以当遇到false
时它无法解析。
data Group = Group [Int] deriving (Eq, Show)
jsonData1 :: ByteString
jsonData1 = [r|
{
"group" : [1, 2, 4]
}
|]
jsonData2 :: ByteString
jsonData2 = [r|
{
"group" : false
}
|]
instance FromJSON Group where
parseJSON = withObject "group" $ \g -> do
items <- g .:? "group" .!= []
return $ Group items
test1 :: Either String Group
test1 = eitherDecode jsonData1
-- returns "Right (Group [1,2,4])"
test2 :: Either String Group
test2 = eitherDecode jsonData2
-- returns "Left \"Error in $.group: expected [a], encountered Boolean\""
我最初希望(.!=)
运算符允许它默认为空列表,但只有在属性完全不存在或null
时才有效。如果它是"group": null
,它将成功解析,我会得到Right (Group [])
。
有关如何让它成功解析并返回空列表的任何建议,在这些情况下它是false
?
解决此问题的一种方法是在对数据集有效的JSON数据构造函数上进行模式匹配,并对所有其他构造函数进行无效。
例如,您可以为该特定字段编写类似的内容,请记住parseJSON
是来自Value -> Parser a
的函数:
instance FromJSON Group where
parseJSON (Bool False) = Group <$> pure []
parseJSON (Array arr) = pure (Group $ parseListOfInt arr)
parseJSON invalid = typeMismatch "Group" invalid
parseListOfInt :: Vector Value -> [Int]
parseListOfInt = undefined -- build this function
你可以在Aeson docs中看到一个这样的例子,这是非常好的(但你必须仔细阅读它们几次)。
然后我可能会定义一个单独的记录来表示此密钥所在的顶级对象并依赖于泛型派生,但其他人可能会在那里有更好的建议:
data GroupObj = GroupObj { group :: Group } deriving (Eq, Show)
instance FromJSON GroupObj
在与Aeson合作时要始终牢记的一件事是core constructors(其中只有6个)和基础数据结构(例如HashMap
的Object
和Vector
的Array
)。
例如,在上面,当你在Array arr
上模式匹配时,你必须意识到你在Vector Value
那里得到了一个arr
,我们还有一些工作要把它变成一个整数列表,这就是为什么我留下其他功能parseListOfInt
未定义上面因为我认为它可能是一个很好的练习来构建它?