0

I am struggling with a function that would take String JSON as an input and return RoseTree structure as an output.

My code is as follows:

import qualified Data.Aeson as JSON
import qualified Data.Text as T
import qualified Data.Aeson.Types as AT
import Control.Applicative
import qualified Data.ByteString.Char8 as BS
import Data.Attoparsec.ByteString.Char8

data RoseTree a = Empty | RoseTree a [RoseTree a]
                   deriving (Show)

instance (Show a) => JSON.ToJSON (RoseTree a) where
    toJSON (RoseTree n cs) =
        JSON.object [T.pack "value" JSON..= show n
        , T.pack "children" JSON..= JSON.toJSON cs] 

instance (Show a, JSON.FromJSON a) => JSON.FromJSON (RoseTree a) where
    parseJSON (JSON.Object o) =
        RoseTree <$> o JSON..: T.pack "value"
        <*> o JSON..: T.pack "children"

parseRoseTreeFromJSON :: (Show a, JSON.FromJSON a) => String -> (RoseTree a)
parseRoseTreeFromJSON json = 
      let bs = BS.pack json in case parse JSON.json bs of
               (Done rest r) -> case AT.parseMaybe JSON.parseJSON r of
                    (Just x) -> x
                    Nothing -> Empty 
               _ -> Empty

It converts RoseTree structure into JSON perfectly fine. For example, when I run JSON.encode $ JSON.toJSON $ RoseTree 1 [], it returns "{\"children\":[],\"value\":\"1\"}", running JSON.encode $ JSON.toJSON $ RoseTree 1 [RoseTree 2 []] returns "{\"children\":[{\"children\":[],\"value\":\"2\"}],\"value\":\"1\"}".

But when I try to supply that JSON I have got on the previous step into the parser, it always returns Empty:

*Main> parseRoseTreeFromJSON "{\"children\":[],\"value\":1}"
Empty

*Main> parseRoseTreeFromJSON "{\"children\":[{\"children\":[],\"value\":\"2\"}],\"value\":\"1\"}"
Empty

Similarly,

*Main> JSON.decode $ BLS.pack "{\"children\":[{\"children\":[],\"value\":\"2\"}],\"value\":\"1\"}"
Nothing

I suspect that my parser is incorrectly defined. But I cannot tell what exactly is wrong. Is the approach I am using the correct one to approach recursive parsing? What would be the right way to do it recursively?

Alec
  • 31,829
  • 7
  • 67
  • 114
altern
  • 5,829
  • 5
  • 44
  • 72

1 Answers1

2

Weird GHCi typing rules strike again! Everything works as expected if you add type annotations:

ghci> :set -XOverloadedStrings
ghci> JSON.decode "{\"children\":[],\"value\":1}" :: Maybe (RoseTree Int)
Just (RoseTree 1 [])

Without knowing what type you are trying to parse (it sees FromJSON a => Maybe a), GHCi defaults a ~ (). You can try this out by testing the FromJSON () instance out and noticing that it succeeds!

ghci> JSON.decode "[]"
Just ()

Couple side notes on your code if this is actually for a project (and not just for fun and learning):

  • I encourage you to check out OverloadedStrings (you can basically drop almost all uses of T.pack from your code since string literals will infer whether they should have type String, lazy/strict Text, lazy/strict ByteString etc.)
  • You can use DeriveGeneric and DeriveAnyClass to get JSON parsing almost for free (although I concede the behavior will be slightly different from what you currently have)

With those suggestions, here is a rewrite of your code:

{-# LANGUAGE OverloadedStrings, DeriveGeneric, DeriveAnyClass #-}

import qualified Data.Aeson as JSON
import qualified Data.ByteString.Lazy.Char8 as BS
import GHC.Generics (Generic)
import Data.Maybe (fromMaybe)

data RoseTree a = RoseTree { value :: a, children :: [RoseTree a] }
                    deriving (Show, Generic, JSON.FromJSON, JSON.ToJSON)

Note that decode from Data.Aeson does (almost) what parseRoseTreeFromJSON does...

ghci> :set -XOverloadedStrings
ghci> JSON.encode (RoseTree 1 [])
"{\"children\":[],\"value\":1}"
ghci> JSON.encode (RoseTree 1 [RoseTree 2 []])
"{\"children\":[{\"children\":[],\"value\":2}],\"value\":1}"
ghci> JSON.decode "{\"children\":[],\"value\":1}" :: Maybe (RoseTree Int)
Just (RoseTree {value = 1, children = []})
ghci> JSON.decode "{\"children\":[{\"children\":[],\"value\":2}],\"value\":1}" :: Maybe (RoseTree Int)
Just (RoseTree {value = 1, children = [RoseTree {value = 2, children = []}]})
Alec
  • 31,829
  • 7
  • 67
  • 114