如何使用Aeson解析JSON值,该值可以是两种不同类型之一

时间:2019-02-17 04:31:00

标签: haskell aeson

我目前正在努力使用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的情况下,如何获得成功解析并返回空列表的任何建议?

1 个答案:

答案 0 :(得分:1)

解决此问题的一种方法是在对您的数据集有效的 JSON数据构造函数上进行模式匹配,并对所有其他数据集抛出 invalid

例如,您可以为该特定字段编写类似的代码,请记住parseJSONValue -> 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和{{1 }},例如Object

例如,在上面的示例中,当您在Vector上进行模式匹配时,您必须知道在Array中有一个Array arr在那里,我们还有一些工作要做这样做以将其转换为整数列表,这就是为什么我在上面未定义其他函数Vector Value的原因,因为我认为构建它可能是一个好练习?