'convert dynamic key fields in JSON
here is my JSON structure, there are N records that has a name as ID to represent a children
{"Kids":
{"Jack":{"age":10}
,"Jane":{"age":9}
, .......
}
}
in the data type in Haskell
data Kid = Kid { name::String, age::Int}
instance FromJSON Kid where
parseJSON (Object v) =
....
question is ,how to make the key ( name ) as part of the constructor ? the expected output signature is like:
decode "input json string" -> [Kid]
when the expect decode function was called, it will return a list of type Kid. Thanks for reading this & appreciate any help .
Solution 1:[1]
By using the withObject function, you get access to an Object which is actually a KeyMap which you can manipulate much like the usual Map from e.g. containers. If you're on an older aeson version, Object will instead be a HashMap, so you can use that as well.
EDIT: I remember that Map itself also has a FromJSON, so you can probably use that instead for a shorter "solution":
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson (FromJSON(..), withObject, (.:), fromJSON)
import Data.Map (Map)
import qualified Data.Map as Map
data Kid = MkKid {name :: String, age :: Int}
newtype Kids = MkKids {unKids :: [Kid]}
instance FromJSON Kids where
parseJSON = withObject "Kids" $ \o -> do
kvmap <- o .: "Kids"
pure $ MkKids $ map (uncurry MkKid) $ Map.toList kvmap
Old "solution", which manipulates they KeyMap
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson.Key (Key)
import qualified Data.Aeson.Key as Key
import qualified Data.Aeson.KeyMap as KeyMap
import Data.Aeson.Types (Parser, Object, Value)
import Data.Aeson (FromJSON(..), withObject, (.:), fromJSON)
data Kid = MkKid {name :: String, age :: Int}
newtype Kids = MkKids {unKids :: [Kid]}
instance FromJSON Kids where
parseJSON = withObject "Kids" $ \o -> do
inner <- o .: "Kids"
withObject "inner" parseKids inner
where
parseKids :: Object -> Parser Kids
parseKids obj =
fmap MkKids $ traverse toKid $ KeyMap.toList obj
toKid :: (Key, Value) -> Parser Kid
toKid (k, v) = do
age <- parseJSON v
let name = Key.toString k
pure $ MkKid {name, age}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
