I'm trying to parse JSON data in haskell. Having gone through a slew of websites, this is the furthest I have been able to get to.
data Address = Address { house :: Integer, street :: String, city :: String, state :: String, zip :: Integer } deriving (Show)
data Person = Person { name :: String, age :: Integer, address :: Address } deriving (Show)
getName :: Person -> String
getName (Person n _ _) = n
getAddress :: Person -> Address
getAddress (Person _ _ a) = a
getState :: Address -> String
getState (Address _ _ _ s _) = s
I write that in a file ex.hs and load it in ghci -->
Prelude> import Text.JSON
Prelude Text.JSON> :load ex
Main Text.JSON> let aa = "{\"name\": \"some body\", \"age\" : 23, \"address\" : {\"house\" : 285, \"street\" : \"7th Ave.\", \"city\" : \"New York\", \"state\" : \"New York\", \"zip\" : 10001}}"
...> decode aa :: Result JSValue
It returns
Ok (JSObject (JSONObject {fromJSObject = [("name",JSString (JSONString {fromJSString = "some body"})),("age",JSRational False (23 % 1)),("address",JSObject (JSONObject {fromJSObject = [("house",JSRational False (285 % 1)),("street",JSString (JSONString {fromJSString = "7th Ave."})),("city",JSString (JSONString {fromJSString = "New York"})),("state",JSString (JSONString {fromJSString = "New York"})),("zip",JSRational False (10001 % 1))]}))]}))
Needless to say, it seems pretty verbose (and frightening). I tried doing
...> decode aa :: Result Person
and it gave me an error. How do I go about populating an instance of the Person datastructure from this json string? For example, what should I do to get the state of the person in the JSON string...
The problem is that
Text.JSON
does not know how to convertJSON
data to yourPerson
data type. To do this, you need to either makePerson
and instance of theJSON
typeclass, or your can useText.JSON.Generic
and theDeriveDataTypeable
extension to do the work for you.Generics
The
Text.JSON.Generic
method will read theJSON
structure based on the structure of your data type.This method works really well as long as you don't mind matching the names of the fields in your data structure to your
JSON
format.As an aside, you don't need to write functions like
getName
,getAddress
, andgetState
. The names of the field in your record type are accesor functions.JSON Instance
Alternatively, you could take the high road and implement your own instance of the
JSON
class.This takes advantage of the fact that the
Result
type is anApplicative
to easily chain together queries on theJSObject
value.This is a little more work, but it gives you more control of the structure of the
JSON
if you have to deal withJSON
that will cause style guideline violations due to weird field names.Maybe a bit late in the game, but since this is the first page google returns I'll give it a go.
Aeson is the defacto standard these days so that's the library everybody uses. The Aeson TH package offers some nice functionality for automatically generating the necessary functions for your custom data types.
Basically you create your data types that correspond to the json data and then let aeson do the magic.
You can even have optional fields with the
Maybe
datatype.