tags:

views:

303

answers:

2

Completely stumped.

Can't make sense of any of the documentation. Can someone please provide an example of how I can parse the following shortened exiftool output using the Haskell module Text.JSON? The data is generating using the command exiftool -G -j <files.jpg>.

[{
  "SourceFile": "DSC00690.JPG",
  "ExifTool:ExifToolVersion": 7.82,
  "File:FileName": "DSC00690.JPG",
  "Composite:LightValue": 11.6
},
{
  "SourceFile": "DSC00693.JPG",
  "ExifTool:ExifToolVersion": 7.82,
  "File:FileName": "DSC00693.JPG",
  "EXIF:Compression": "JPEG (old-style)",
  "EXIF:ThumbnailLength": 4817,
  "Composite:LightValue": 13.0
},
{
  "SourceFile": "DSC00694.JPG",
  "ExifTool:ExifToolVersion": 7.82,
  "File:FileName": "DSC00694.JPG",
  "Composite:LightValue": 3.7
}]
+7  A: 

Well, the easiest way is to get back a JSValue from the json package, like so (assuming your data is in text.json):

Prelude Text.JSON> s <- readFile "test.json"
Prelude Text.JSON> decode s :: Result JSValue
Ok (JSArray [JSObject (JSONObject {fromJSObject = [("SourceFile",JSString (JSONString {fromJSString = "DSC00690.JPG"})),("ExifTool:ExifToolVersion",JSRational False (391 % 50)),("File:FileName",JSString (JSONString {fromJSString = "DSC00690.JPG"})),("Composite:LightValue",JSRational False (58 % 5))]}),JSObject (JSONObject {fromJSObject = [("SourceFile",JSString (JSONString {fromJSString = "DSC00693.JPG"})),("ExifTool:ExifToolVersion",JSRational False (391 % 50)),("File:FileName",JSString (JSONString {fromJSString = "DSC00693.JPG"})),("EXIF:Compression",JSString (JSONString {fromJSString = "JPEG (old-style)"})),("EXIF:ThumbnailLength",JSRational False (4817 % 1)),("Composite:LightValue",JSRational False (13 % 1))]}),JSObject (JSONObject {fromJSObject = [("SourceFile",JSString (JSONString {fromJSString = "DSC00694.JPG"})),("ExifTool:ExifToolVersion",JSRational False (391 % 50)),("File:FileName",JSString (JSONString {fromJSString = "DSC00694.JPG"})),("Composite:LightValue",JSRational False (37 % 10))]})])

this just gives you a generic json Haskell data type.

The next step will be to define a custom Haskell data type for your data, and write an instance of JSON for that, that converts between JSValue's as above, and your type.

Don Stewart
I originally posted a separate answer about the same time, but I find I like the wording on this one better... Oh well. I'll just add my €0.02 as a comment: have a look at the file `Text/JSON.hs` in json-the-package's sources. There's a whole bunch of simple `JSON` instances defined there and they can serve as useful examples.
Michał Marczyk
Nice answer! Then again I would expect nothing less from a co-author of 'Real World Haskell' (http://book.realworldhaskell.org/). Which is a really cool book btw.
Robert Massaioli
A: 

Thanks to all. From your suggestions I was able to put together the following which translates the JSON back into name-value pairs.

data Exif = 
    Exif [(String, String)]
    deriving (Eq, Ord, Show)

instance JSON Exif where
    showJSON (Exif xs) = showJSONs xs
    readJSON (JSObject obj) = Ok $ Exif [(n, s v) | (n, JSString v) <- o]
        where 
            o = fromJSObject obj
            s = fromJSString

Unfortunately, it seems the library is unable to translate the JSON straight back into a simple Haskell data structure. In Python, it is a one-liner: json.loads(s).

me2
Well, it has to be statically typed. So the Haskell one builds a "simple" JSValue type -- which you can work with directly if you wish.The benefit of using Haskell is the extra static checking you'll get if you in turn build a specific structure.
Don Stewart