WellTyped WellTyped - 3 months ago 22
JSON Question

Haskell Aeson nested array JSON

I have trouble parsing the Tag in the following JSON structure. The parser works only when i declare it to be

tags :: !Array
It fails when i declare it as
tags :: [Tag]


Why?

{
"response": {
"status": "ok",
"results": [
{
"type": "article",
"fields": {
"wordcount": "497"
},
"tags": [
{
"id": "profile/barryglendenning"
}
]
}
]
}
}



data Field = Field{
wordcount :: Int
} deriving (Show)

instance FromJSON Field where
parseJSON (Object o) = Field <$> (o .: "wordcount")
parseJSON _ = mzero


data Tag = Tag{
id :: Text
} deriving (Show)

instance FromJSON Tag where
parseJSON (Object o) = Tag <$> (o .: "id")
parseJSON _ = mzero

data SearchResult = SearchResult {
type:: Text,
field :: Field,
tags :: !Array
} deriving (Show)

instance FromJSON SearchResult where
parseJSON (Object o) = do
let t1 = o .: "type"
let t2 = o .: "fields"
let t3 = o .: "tags"
SearchResult <$> t1 <*> t2 <*> t3
parseJSON _ = mzero


data ContentrResult = ContentrResult {
results :: [SearchResult],
status :: Text
} deriving (Show)

instance FromJSON ContentrResult where
parseJSON (Object o) = do
r <- o .: "response"
ContentrResult <$> r .: "results"
<*> r .: "status"
parseJSON _ = mzero

Answer

Nothing isn't a very useful for debugging is it?

I managed to get your example JSON parsing the tags as a [Tag]. I wonder if your error could be related to the wordcount field being a String in the JSON rather than a Number.

Here is a more-or-less self-contained example where I changed the example JSON wordcount to a number:

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}

module Main where

import Lib (str)

import Control.Monad (mzero)
import Data.Aeson
import qualified Data.ByteString.Lazy.Char8 as LBSC
import Data.Text

data Field = Field {
  wordcount :: Int
} deriving (Show)

instance FromJSON Field where
  parseJSON (Object o) = Field <$> o .: "wordcount"
  parseJSON _ = mzero

data Tag = Tag {
  id :: Text
} deriving (Show)

instance FromJSON Tag where
  parseJSON (Object o) = Tag <$> (o .: "id")
  parseJSON _ = mzero

data SearchResult = SearchResult {
  typ :: Text,
  fields :: Field,
  tags :: [Tag]
} deriving (Show)

instance FromJSON SearchResult where
  parseJSON (Object v) = SearchResult <$> v .: "type" <*> v .: "fields" <*> v .: "tags"
  parseJSON _ = mzero

data ContentrResult = ContentrResult {
  results :: [SearchResult],
  status :: Text
} deriving (Show)

instance FromJSON ContentrResult where
  parseJSON (Object v) = ContentrResult <$> v.: "results" <*> v .: "status"
  parseJSON _ = mzero

data Response = Response {
  response :: ContentrResult
} deriving (Show)

instance FromJSON Response where
  parseJSON (Object v) = Response <$> v .: "response"
  parseJSON _ = mzero

responseJson :: String
responseJson = [str|
  {
    "response": {
      "status": "ok",
      "results": [
        {
          "type": "article",
          "fields": {
            "wordcount": 497
          },
          "tags": [
            {
              "id": "profile/barryglendenning"
            }
          ]
        }
      ]
    }
  }
|]

main :: IO ()
main = do
  print r
  putStrLn ""
    where
      r :: Maybe Response
      r = decode (LBSC.pack responseJson)