1

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.

Community
  • 1
  • 1
Noah Blues
  • 1,339
  • 2
  • 11
  • 19

2 Answers2

2

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.

amindfv
  • 8,438
  • 5
  • 36
  • 58
Itai Zukerman
  • 483
  • 2
  • 7
  • For completeness/total-ness, there should be a `_ -> Error "Unrecognised Foo"` pattern in the `case`. – huon Dec 29 '12 at 21:04
  • It's great to define certain field like you do, but if there are lots of uncertain fields? The fields may __vary every time__ when I received responses from some API, and I can't even be sure what the fields are. – Noah Blues Dec 31 '12 at 22:06
2

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.

Noah Blues
  • 1,339
  • 2
  • 11
  • 19