使用 Aeson 解析嵌套数组和对象

Parsing nested arrays and objects with Aeson

我在编写 FromJSON 实例来解析包含嵌套在另一个对象数组中的对象数组的 JSON 文件时遇到问题。该文件的格式为:

[{"family":[{"name":"Jane","age":31,}
           ,{"name":"Ana","age":15,}]}
,{"family":[{"name":"Julia","age":45}
           ,{"name":"Chris","age":47}]}]

为此我创建了两个新类型:

-- A person
data Person = Person { name :: String
                     , age  :: Int} deriving (Show, Eq)

-- A family is a list of list of Person
newtype Family = Family {unFamily :: [[Person]]} deriving (Show, Eq)

它们的实例是这些(使用 XOverloadedStringsaesontext):

instance ToJSON Person where
  toJSON (Person n a) = object [ "name" .= n
                               , "age"  .= a]
instance FromJSON Person where
  parseJSON (Object o) = Person
    <$> o .:? "name" .!= ""
    <*> o .:? "age"  .!= 0
  parseJSON _ = mzero

instance ToJSON Family where
  toJSON (Family c) = toJSON . map (\v -> object $ ("family",v):[]) 
                             $ map toJSON c

我可以做一些测试(加载 bytestringmaybe

> julia = Person "Julia" 45
> (== julia) . fromMaybe (Person "" 0) . decode . encode $ julia
True
> BS.putStrLn . BL.toStrict . encode $ Family [[julia,julia],[julia]]
[{"family":[{"age":45,"name":"Julia"},{"age":45,"name":"Julia"}]},{"family":[{"age":45,"name":"Julia"}]}]

一切都如我所愿。但正如我所说,我不能写 FromJSON Family,所以我不能像 decode . encode 那样测试 julia

我查看了 aeson 中涉及的许多类型,试图找到一种方法让它发挥作用,但我就是做不到。我至少可以为 ToJSON FamilytoJSON 因为我了解到 type Pair = (Text, Value).

谁能给我指明方向?有没有更好的写法 ToJSON Family?

我会写

newtype Family = Family {unFamily :: [Person]}

相反。那么

instance FromJSON Family where
    parseJSON (Object o) = Family <$> (o .: "family")
    parseJSON v = typeMismatch "family" v
instance ToJSON Family where
    toJSON (Family c) = "family" .= c

应该对实例起作用,而您为 [Family] 免费获得的 FromJSON/ToJSON 实例将解码您展示的示例 JSON us/encode 您向我们展示的示例家庭。

你可以创建一个解析子列表的函数,然后使用mapM转换子列表:

import Data.Foldable(toList)

instance FromJSON Family where
    parseJSON (Array arr) = Family <$> mapM parseSubList (toList arr)
        where parseSubList (Object o) = o .:? "family" .!= []

例如:

Prelude Data.Aeson Data.Foldable> decode "[{\"family\":[{\"age\":45,\"name\":\"Julia\"},{\"age\":45,\"name\":\"Julia\"}]},{\"family\":[{\"age\":45,\"name\":\"Julia\"}]}]" :: Maybe Family
Just (Family {unFamily = [[Person {name = "Julia", age = 45},Person {name = "Julia", age = 45}],[Person {name = "Julia", age = 45}]]})