Noah Blues
Noah Blues

Reputation: 1359

How to parse uncertain data from JSON in Haskell?

I used the answer in json_answer (Text.JSON package) and I've got a generic json Haskell data type. It's ok to define a custom Haskell data type for certain data, but if the data I want to parse is uncertain,

For example, if I got a response A from some APIs, the field is "category" this time:

[JSObject (JSONObject {fromJSObject = [("category",JSString (JSONString {fromJSString = "photo"}))]})]

And next time, "address":

[JSObject (JSONObject {fromJSObject = [("address",JSString (JSONString {fromJSString = "http://www.example.com"}))]})]

or some other uncertain fields, and the generic json type may be nested.

how can I extract it from the json data type?

Thanks for your help.

Upvotes: 1

Views: 339

Answers (2)

Noah Blues
Noah Blues

Reputation: 1359

I used Data.Aeson library and it solved my question.

decode in Data.Aeson can produce a Maybe Value result. And I used pattern matching to extract the values from the Maybe Value result like follow:

json2Array :: Maybe Value -> Array
json2Array (Just (Array anArray)) = anArray

arrayValue2Object :: Value -> Object
arrayValue2Object (Object anObject) = anObject

And in Data.Aeson, Object constructor is a synonym of HashMap Text Value so I can use the Data.HashMap.Lazy/Strict to extract the values I need.

This method may not good, but it realy helped me.

Upvotes: 2

Itai Zukerman
Itai Zukerman

Reputation: 483

Just looking at the documentation for Text.JSON, something like this might work for you:

import Text.JSON

data Foo = Category String
         | Address String
         deriving (Show)

toJSO = JSObject . toJSObject

instance JSON Foo where
    showJSON (Category s) = toJSO [("category", showJSON s)]
    showJSON (Address s)  = toJSO [("address",  showJSON s)]
    readJSON (JSObject obj) = case o of
      [("category", JSString s)] -> Ok $ Category $ fromJSString s
      [("address",  JSString s)] -> Ok $ Address  $ fromJSString s
      where
        o = fromJSObject obj

Basically this says: if the JSON object has just a "category" field then it's a Category, and same for "address". To really make this work you'd add another clause to the case and the readJSON for indicating a "parse" error.

Then to parse your example, you'd do:

decode "some-JSON-here" :: Result [Foo]

which magically works because of this instance:

JSON a => JSON [a]

There are probably better ways of doing this, but this worked for me and seems pretty straightforward.

Upvotes: 2

Related Questions