[Haskell-beginners] Reading JSON using Text.JSON

Adrien Haxaire adrien at adrienhaxaire.org
Mon May 16 10:44:32 CEST 2011


 Wow, that's a very nice tutorial, thanks a lot Christopher for taking 
 time to explain it to me with so much details. Í really appreciate it.

 I will try to have it working tonight (ie 20:00 GMT +1) and let you 
 know.

 Michael, aeson looks interesting. I may give it a try if/when needed, 
 but for know I prefer to focus on the Text.JSON module to learn Haskell, 
 as it is the default one.

 I keep you updated as soon as I have it working (or not), and thanks 
 again for the help !

 Adrien




 On Mon, 16 May 2011 01:37:50 +0200, Christopher Done wrote:
> 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 Coordin
>
>> d; padding-left: 1ex; ">        
> Eq, Ord, Show)
>
> As far as implementing an instance for your data type goes, you write
> it like this:
>
> instance JSON Node where
>   readJSON o
>
>> (Node coords integer) = makeObj [("co
> t;,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  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




More information about the Beginners mailing list