watain
watain

Reputation: 5098

Aeson: parse JSON with unknown key in Haskell

I would like to parse the following JSON using Aeson in Haskell:

{
    "foo": {
        "name": "name 1",
        "location": "location 1"
    },
    "bar": {
        "name": "name 2",
        "location": "location 2"
    }
}

The keys name and location are known, but foo and bar are unknown.

I would like to load the JSON data as a list of the following data type ([Entry]):

data Entry = Entry
        { id :: String       -- "foo" or "bar" etc.
        , name :: String     -- "name 1" or "name 2" etc.
        , location :: String -- "location 1" or "location 2" etc.
        } deriving Show

My first try looks as follows (it does not work, yet):

instance FromJSON Entry where
        parseJSON (Object o) = do
                map (\(id, (name, location)) -> Entry id name location) o
                parseJSON o

How would I parse the JSON correctly?

Upvotes: 3

Views: 895

Answers (1)

kosmikus
kosmikus

Reputation: 19637

This should work:

{-# LANGUAGE FlexibleInstances, OverloadedStrings #-}

import Data.Aeson
import Data.Aeson.Types
import Data.HashMap.Strict

data Entry = Entry
  { id :: String
  , name :: String
  , location :: String
  }
  deriving Show

instance FromJSON [Entry] where
  parseJSON x =
    parseJSON x >>= mapM parseEntry . toList

parseEntry :: (String, Value) -> Parser Entry
parseEntry (i, v) =
  withObject "entry body" (\ o ->
    Entry i <$> o .: "name" <*> o .: "location")
    v

Considering the way you want to parse entries, you cannot really parse single entries, only several at once, because you need the outer object structure.

The JSON parser for [Entry] then proceeds by first parsing the outer object as a hashmap and turning that into a list of pairs using toList, then processing each resulting (String, Value) pair with parseEntry.

Upvotes: 6

Related Questions