
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:
It seems I have to define an instance of JSON to read it. Does
> The problem is that I have no idea what to do with decoded JSValue. 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