[Haskell-beginners] Reading JSON using Text.JSON

Christopher Done chrisdone at googlemail.com
Mon May 16 01:37:50 CEST 2011


On Sun, May 15, 2011 at 09:51:28PM +0200, Adrien Haxaire wrote:

>  > The problem is that I have no idea what to do with decoded JSValue.
> > It seems I have to define an instance of JSON to read it. Does this
> > mean using pattern matching to extract the values ?
>

It's correct that you should define an instance of JSON to have a to/from
JSON mapping for your data type. It's not required, but it's a very good
pattern. All parsing is done using the instances. As you can see,

λ> :t decode
decode :: (JSON a) => String -> Result a

internally, this function uses the JSON class's readJSON method. The return
value is thus polymorphic.

λ> decode "1" :: Result JSValue
Ok (JSRational False (1 % 1))
λ> decode "1" :: Result Integer
Ok 1

I have this JSON string that I have put in a text file, test.json:
>
> {"coordinates": [0.0, 1.0]}
>

There is a tuple instance of JSON that you can use to parse the JSON tuple.

instance (JSON a, JSON b) => JSON (a, b)

λ> decode "[1,2]" :: Result (Double,Double)
Ok (1.0,2.0)

And for parsing objects, you use the JSObject data type:

λ> decode "{\"coordinates\":[1,2]}" :: Result (JSObject (Integer,Integer))
Ok (JSONObject {fromJSObject = [("coordinates",(1,2))]})

Once you're done parsing you have either Ok or Error and you handle it
accordingly.

data Node = Node Coordinates Number

         deriving (Eq, Ord, Show)


As far as implementing an instance for your data type goes, you write it
like this:

instance JSON Node where
  readJSON object = do obj <- readJSON object
                       coords <- valFromObj "coordinates" obj
                       return (Node coords 0)
  showJSON (Node coords integer) = makeObj [("coordinates",showJSON coords)]

And then you can use decode for your data type:

λ> decode "{\"coordinates\":[1,2]}" :: Result Node
Ok (Node (1.0,2.0) 0)
λ> encode (Node (1.0,2.0) 0)
"{\"coordinates\":[1,2]}"

To make the code nicer to read and feel more declarative, you can use the
Applicative instance of JSON:

  readJSON object = do obj <- readJSON object
                       Node <$> valFromObj "coordinates" obj
                            <*> pure 0

This is equivalent to the lines above. It's nicer like this because with
more parameters to the type, it still remains readable. Let's say you want
to include the number in the JSON parsing, no problem:

  readJSON object = do obj <- readJSON object
                       Node <$> valFromObj "coordinates" obj
                            <*> valFromObj "number" obj

λ> decode "{\"coordinates\":[1,2],\"number\":42}" :: Result Node
Ok (Node (1.0,2.0) 42)

Or let's say you want to make number optional. That's easy with the
Alternative instance of JSON:

  readJSON object = do obj <- readJSON object
                       Node <$> valFromObj "coordinates" obj
                            <*> (valFromObj "number" obj <|> pure 0)

λ> decode "{\"coordinates\":[1,2],\"number\":42}" :: Result Node
Ok (Node (1.0,2.0) 42)
λ> decode "{\"coordinates\":[1,2]}" :: Result Node
Ok (Node (1.0,2.0) 0)

And finally, for extracting the value, as you basically figured out above,
you just pattern match on the Ok/Error:

λ> case decode "{\"coordinates\":[1,2]}" of
     Ok (Node (x,y) n) -> do putStrLn $ "Coords: " ++ show (x,y)
                             putStrLn $ "Number: " ++ show n
     Error msg         -> do putStrLn $ "Failed: " ++ msg
Coords: (1.0,2.0)
Number: 0

That's pretty much the whole pattern.

Or if you hate pattern matching, you can define a maybe/either-like
function:

result :: (String -> b) -> (a -> b) -> Result a -> b
result f g (Error x) = f x
result f g (Ok a) = g a

λ> result (const $ putStrLn ":(") (putStrLn.show) $ (decode
"{\"coordinates\":[1,2]}" :: Result Node)
Node (1.0,2.0) 0
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.haskell.org/pipermail/beginners/attachments/20110516/72bd8714/attachment-0001.htm>


More information about the Beginners mailing list