如何使用可以是两种不同类型之一的 Aeson 解析 JSON 字符串

How to parse a JSON string using Aeson that can be one of two different types

我目前正在努力使用 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 []).

关于如何让它成功解析的任何建议和 return 在这些情况下 false 的空列表?

解决此问题的一种方法是对对您的数据集有效的 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 中看到这方面的示例,非常好(但您必须仔细阅读它们并通读几遍)。

然后我可能会定义一个 separate 记录来表示该密钥所在的顶级对象并依赖于泛型派生,但其他人可能有更好的建议:

data GroupObj = GroupObj { group :: Group } deriving (Eq, Show)
instance FromJSON GroupObj

使用 Aeson 时要始终牢记的一件事是 core constructors(其中只有 6 个)和底层数据结构(HashMap 用于 ObjectVector 代表 Array,例如)。

例如,在上面,当你在 Array arr 上进行模式匹配时,你必须意识到你在 arr 中得到了一个 Vector Value 并且我们仍然有需要做一些工作才能将其转换为整数列表,这就是为什么我在上面未定义另一个函数 parseListOfInt 的原因,因为我认为构建它可能是一个很好的练习?