Trying to parse recursive JSON, am I on the right track? - json

This problem is related to this question.
Here is the data type I wish to make from the JSON:
data ProdObject = MKSpair (Text, Text)
| MKSLpair (Text, [Text])
| MKSOpair (Text, ProdObject)
| MKObject ProdObject
| End
deriving Show
Here is a sample of the data I am working with, plus a generalization of the whole.
Here's my instance definition, which is causing an error. I've used this as a reference. I'm not sure if the error is telling me to fix my type, or that I'm way off. If the error really is straight-forward, I'd like some advice on how to fix my type,plus any advice on what else I may be doing wrong but haven't noticed yet.
instance FromJSON ProdObject where
parseJSON (Object o) = MKObject <$> parseJSON o
parseJSON (String s, String t) = MKSpair (s, t)
parseJSON (String s, Object o) = MKSOpair (s, MKObject <$> parseJSON o)
parseJSON (String s, Array a) = MKSLpair (s, V.toList a)
parseJSON (Done d) = End
parseJSON _ = mzero
Here's the error I have right now:
ghcifoo> :load test
[1 of 1] Compiling Main ( test.hs, interpreted )
test.hs:23:52:
Couldn't match expected type `Value'
with actual type `Data.Map.Map Text Value'
Expected type: Value
Actual type: Object
In the first argument of `parseJSON', namely `o'
In the second argument of `(<$>)', namely `parseJSON o'
Failed, modules loaded: none.
Update: I've redone my data type, if I'm right I've got a phantom type. If I'm wrong, back to the drawing board
data ProdObject = MKSpair (Text, Text)
| MKSLpair (Text, [Text])
| MKSOpair (Text, ProdObject)
| MKObject ProdObject (k,v)
| End
Also I have reflected this change in my instance, though in an incomplete manner. I mention this just to ask if I am on the right track or not.
parseJSON (Object (k,v)) = MKObject ...
If I'm on the right track, I think I can either figure out the rest, or at least ask a specific question. Feedback anyone?

In this equation:
parseJSON (Object o) = MKObject <$> parseJSON o
o has type Map Text Value, but parseJSON has type Value -> Parser a, so you obviously can't apply parseJSON to o.
Also, you have a type error here:
parseJSON (String s, String t) = MKSpair (s, t)
The type of parseJSON is Value -> Parser a, but you are trying to match against (Value, Value).
The same applies to this line:
parseJSON (Done d) = End
Done is not a constructor of type Value.
I couldn't understand why you need the ProdObject type to be recursive; here's how I would solve this problem:
data Outer = Outer {
oName :: Text,
oProducts :: M.Map Text Inner
} deriving Show
data Inner = Inner {
iQA :: Text,
iVM :: Text,
iAvailable :: V.Vector Text
} deriving Show
instance FromJSON Outer where
parseJSON (Object o) = Outer <$> o .: "name" <*> o .: "products"
parseJSON _ = mzero
instance FromJSON Inner where
parseJSON (Object o) = Inner <$> o .: "qa" <*> o .: "vm" <*> o .: "available"
parseJSON _ = mzero
Full code listing can be found on Github.

Related

Parsing "the rest" of an aeson object

for some reason I can't wrap my head around arbitrarilly successful parses in Aeson, without making the whole system bork and cause a space leak.
Here's my issue:
newtype Foo = Foo
{ getFoo :: [(String, Maybe String)]
} deriving (Show, Eq)
instance ToJSON Foo where
toJSON (Foo xs) = object $
map (\(k,mv) -> T.pack k .= mv) xs
so far, encoding a Foo is fine and dandy. But, I want to make a parser that rejects a couple of keys, if they exist. Right now, I have a pseudo-rejection going on, and that's why I think I'm getting a bad outcome:
import qualified Data.HashMap as HM
-- the "duck-tape and chewing gum" approach
instance FromJSON Foo where
parseJSON (Object o) = Foo <$> parseJSON (Object theRest)
where
theRest = foldr HM.delete o [ "foo"
, "bar"
]
parseJSON _ = empty
This version is what caused me to think that manipulating the internal object was incorrect, because the parser may be getting "more" data in the HashMap, outside of the parser (because of the lazy bytestring being fed into it), but I am clearly not sure about this. So, I tried a different approach:
instance FromJSON Foo where
parseJSON (Object o) =
(Foo . filter (\(k,_) -> k `elem` toIgnore)) <$>
parseJSON (Object o)
where
toIgnore = ["foo", "bar"]
parseJSON _ = empty
But this also seems to cause a deadlock / space leak (not sure exactly what to diagnose this halting of execution). What would be the advised way to accept everything except a few keys of the object? I need to pattern-match on the (Object o) structure because I'm manually looking up o .: "foo" and o .: "bar" in a different component for my data type. Ideally, I would like to just remove those keys from the content and continue parsing, because I already accounted for them (hence - "the rest").
Is there any hope?
For your PartialAppContact example here is a more mundane approach which seems to work:
{-# LANGUAGE OverloadedStrings, QuasiQuotes #-}
import Data.Aeson
import qualified Data.Text as T
import qualified Data.HashMap.Strict as HM
import Control.Monad
import Text.Heredoc
type RequiredField = String
type OptionalField = String
data PartialAppContact = PartialAppContact
{ partialAppContactRequired :: [(RequiredField, String)]
, partialAppContactOptional :: [(OptionalField, Maybe String)]
} deriving (Show, Eq)
instance FromJSON PartialAppContact where
parseJSON (Object o) = do
let required = [ "firstName", "lastName", "email", "phoneNumber" ]
reqPairs <- forM required $ \k -> do
v <- o .: k
s <- parseJSON v
return (T.unpack k, s)
nonReqPairs <- forM [ (k,v) | (k,v) <- HM.toList o, k `notElem` required ] $ \(k,v) -> do
s <- parseJSON v
return (T.unpack k, s)
return $ PartialAppContact reqPairs nonReqPairs
test1 = Data.Aeson.eitherDecode "{\"firstName\":\"Athan'\"}" :: Either String PartialAppContact
input = [str|
| { "firstName": "a first name"
| , "lastName": "a last name"
| , "email": "asasd#asd.com"
| , "phoneNumber": "123-123-123"
| , "another field": "blah blah" }
|]
test2 = Data.Aeson.eitherDecode "{\"firstName\":\"Athan'\" }" :: Either String PartialAppContact
test3 = Data.Aeson.eitherDecode input :: Either String PartialAppContact
Update
Based on your comments, consider this idea for writing the instance:
import Data.List (partition)
instance FromJSON PartialAppContact where
parseJSON (Object o) = do
let required = [ "firstName", "lastName", "email", "phoneNumber" ]
let (group1, group2) = partition (\(k,_) -> k `elem` required) (HM.toList o)
reqFields <- forM group1 $ \(k,v) -> do s <- parseJSON v; return (T.unpack k, s)
otherFields <- forM group2 (\(k,v) -> (T.unpack k,) <$> parseJSON v)
return $ PartialAppContact reqFields otherFields
I found a working implementation requires the use of (.:?), to correctly implement optional, known fields. From there, you can freely decompose the HashMap and re-parseJSON it's subfields:
instance FromJSON Foo where
parseJSON (Object o) = do
mfoo <- o .:? "foo"
mbar <- o .:? "bar"
let foundFields = catMaybes [mfoo, mbar]
rest <- mapM (\(k,v) -> (T.unpack k,) <$> parseJSON v)
(toList theRest)
return $ Foo rest -- assuming you're done with `foundFields`
where
theRest = foldr HM.delete o ["foo", "bar"]
To see the final implementation of the issue discussed in the comments, see this commit.

How to parse an array with Haskell Aeson

I have a JSON doc that looks like:
{ "series": [[1,2], [2,3], [3,4]] }
I'd like to parse this into a set of data types:
data Series = Series [DataPoint]
data DataPoint = DataPoint Int Int -- x and y
I'm having lots of problems trying to write the FromJSON instance for DataPoint.
instance FromJSON DataPoint where
parseJSON (Array a) = ???
I've tried using Lens to destruct the DataPoint record, but it doesn't compile:
case a ^.. values . _Integer of -}
[x,y] -> DataPoint <$> x <*> y
_ -> mzero
That fails with this error (the first two lines I get even absent the lens trickery, just trying to create a DataPoint <$> 1 <*> 2):
Couldn't match type ‘aeson-0.7.0.6:Data.Aeson.Types.Internal.Parser
Integer’
with ‘Integer’
Expected type: (aeson-0.7.0.6:Data.Aeson.Types.Internal.Parser
Integer
-> Const
(Data.Monoid.Endo
[aeson-0.7.0.6:Data.Aeson.Types.Internal.Parse
(aeson-0.7.0.6:Data.Aeson.Types.Internal.Parser I
-> Value
-> Const
(Data.Monoid.Endo
[aeson-0.7.0.6:Data.Aeson.Types.Internal.Parser
Value
Actual type: (Integer
-> Const
(Data.Monoid.Endo
[aeson-0.7.0.6:Data.Aeson.Types.Internal.Parse
Integer)
-> Value
-> Const
(Data.Monoid.Endo
[aeson-0.7.0.6:Data.Aeson.Types.Internal.Parser
Value
In the second argument of ‘(.)’, namely ‘_Integer’
In the second argument of ‘(^..)’, namely ‘values . _Integer’
Is there a better way to do this?
Does anybody have an example of parsing arrays of values into a more detailed structure?
Aeson have instance for list, so I think it is not necessary to deal with vectors.
{-# LANGUAGE LambdaCase #-}
import Data.Aeson
data Series = Series [DataPoint]
data DataPoint = DataPoint Int Int
instance FromJSON DataPoint where
parseJSON jsn = do
[x,y] <- parseJSON jsn
return $ DataPoint x y
instance FromJSON Series where
parseJSON = \case
Object o -> (o .: "series") >>= fmap Series . parseJSON
x -> fail $ "unexpected json: " ++ show x
The trick here is getting the instance for FromJSON DataPoint correct, which takes a little bit of matching but isn't too bad. I came up with
instance FromJSON DataPoint where
parseJSON (Array v)
| V.length v == 2 = do
x <- parseJSON $ v V.! 0
y <- parseJSON $ v V.! 1
return $ DataPoint x y
| otherwise = mzero
parseJSON _ = mzero
Which will fail to parse cleanly if it isn't able to pull two Ints out for x and y. Then you just have to define the instance for Series:
instance FromJSON Series where
parseJSON (Object o) = do
pts <- o .: "series"
ptsList <- mapM parseJSON $ V.toList pts
return $ Series ptsList
parseJSON _ = mzero
Which, again, will cleanly fail if the data is malformed anywhere. To test:
> decode "{\"series\": [[1, 2], [3, 4]]}" :: Maybe Series
Just (Series [DataPoint 1 2, DataPoint 3 4])
> decode "{\"series\": [[1, 2], [3, {}]]}" :: Maybe Series
Nothing
So it looks like it works.
EDIT: As #maxtaldykin has pointed out, you can just take advantage of the FromJSON a => FromJSON [a] instance with
instance FromJSON DataPoint where
parseJSON obj = do
[x, y] <- parseJSON obj
return $ DataPoint x y
instance FromJSON Series where
parseJSON (Object o) = do
pts <- o .: "series"
fmap Series $ parseJSON pts
parseJSON _ = mzero
Which is greatly simplified from my original answer. Kudos to Max.

Parsing nested JSON with "random" integer keys using aeson

I am using aeson library for generating and parsing json-files for my custom Graph type. Here are type definitions.
type Id = Int
type Edge = (Id, Id)
type Scenario = [Id]
data Point = Point Int Int
data Vertex = Vertex {-# UNPACK #-}!Id {-# UNPACK #-}!Point deriving (Show)
data Graph = Graph Id [Vertex] Scenario deriving (Show)
Actually I am working with Eulerian and semi-Eulerian graphs, all vertices of which have positions in 2D-space. In a nutshell Graph uses Data.Graph, but this is not related to my problem. Every graph has it's own ID to quickly identify it among many others.
Here is an example of json-file, containing info about my graph:
{
"id": 1,
"vertices": {
"3": {
"y": 12,
"x": 0
},
"2": {
"y": 16,
"x": 24
},
"1": {
"y": 12,
"x": 10
}
},
"scenario": [
1,
2,
3,
1
]
}
So, here is my implementation of toJSON function:
import qualified Data.Text as T
instance ToJSON Graph where
toJSON (Graph id v s) = object [ "vertices" .= object (map vertexToPair v)
, "scenario" .= s
, "id" .= id
]
where
vertexToPair :: Vertex -> (T.Text, Value)
vertexToPair (Vertex id (Point x y)) =
(T.pack $ show id) .= object [ "x" .= x, "y" .= y]
But I actually have a problem with parsing back from json-file. The main problem is the fact, that we don't know how much vertices has particular Graph, so it can't be hard-coded. Here is my first attempt to write parseJSON function:
instance FromJSON Graph where
parseJSON (Object v) = do
i <- parseJSON =<< v .: "id"
vs <- parseJSON =<< v .: "vertices"
sc <- parseJSON =<< v .: "scenario"
maybeReturn ((buildGraph i sc) <$> (parseVertices vs 1))
where
parseVertices :: Value -> Int -> Maybe [Vertex]
-- parseVertices (Object o) i = ???
parseVertices _ _ = Just []
buildGraph :: Int -> Scenario -> [Vertex] -> Graph
buildGraph i sc vertices = Graph i vertices sc
maybeReturn Nothing = mzero
maybeReturn (Just x) = return x
parseJSON _ = mzero
Actually I thought that I can start counting from 1 and get vertices while program still parses every next i. But this is not good choice because minimal vertex id is not always 1, and sometimes next vertex id differs from current by more then 1. Is it even possible to parse such data? Anyway, I stuck even with a simplest case of this problem (when vertex ids start from 1 and are incremented using (+1)).
Alright. This is how I can get max and min vertex id:
import qualified Data.Text.Read as TR
import qualified Data.Foldable as Foldable
minID :: [Either T.Text Int] -> Int
minID = Foldable.maximum
maxID :: [Either T.Text Int] -> Int
maxID = Foldable.minimum
ids :: Object -> [Either T.Text Int]
ids o = map ((fmap fst) . TR.decimal) (M.keys o)
All signatures are not generalised, but this is just example.
I will try tomorrow once again to solve this simple case of problem. Anyway, main question still needs an answer :)
The edit to your answer shows you understood how to solve your immediate problem. Still, you can make your code a lot clearer by avoiding most of the explicit list manipulation needed to build the vertexes. The plan is:
Define a FromJSON instance for Point;
Use it to define a FromJSON instance for Vertex. That would go rather like the Rule instance in other answer to the question you linked to, except that, since you want to use the object keys as IDs, the case statement there would become something like:
case M.toList (o :: Object) of
[(rawID, rawPoint)] -> Vertex (TR.decimal rawId) <$> parseJSON rawPoint
_ -> fail "Rule: unexpected format"
Finally, your existing FromJSON Graph instance will, I believe, work straight away if you change the (inferred) type of vs to [Vertex], given the instance FromJSON a => FromJSON [a]. Therefore, you won't need parseVertices anymore.
If you have control over the JSON structure, it might make sense to simplify things even further by making the vertex IDs a field alongside x and y, removing one level of nesting.
Update: An implementation of the instances, based on the one you added to your answer:
instance FromJSON Point where
parseJSON (Object v) = liftM2 Point (v .: "x") (v .: "y")
parseJSON _ = fail "Bad point"
instance FromJSON [Vertex] where
parseJSON j = case j of
(Object o) -> mapM parseVertex $ M.toList o
_ -> fail "Bad vertices"
where
parseVertex (rawID, rawPoint) = do
let eID = TR.decimal rawID
liftM2 Vertex (either (fail "Bad vertex id") (return . fst) eID) $
parseJSON rawPoint
instance FromJSON Graph where
parseJSON (Object v) = do
i <- parseJSON =<< v .: "id"
vs <- parseJSON =<< v .: "vertices"
sc <- parseJSON =<< v .: "scenario"
return $ Graph i vs sc
parseJSON _ = fail "Bad graph"
(Get the implementation as a runnable example)
The differences to your version are:
You do not need to define an instance for [Graph]; if you define the Graph instance aeson will handle the lists (i.e. JS arrays) automatically (note that the FromJSON documentation mentions a FromJSON a => FromJSON [a] instance. Unfortunately we cannot do the same (at least not as easily) with [Vertex], given that the vertex IDs are keys and not part of the values.
I Added fail cases for the pattern match failures, in order to get more informative error messages.
On your observation about creating the vertices from Either values: your solution was pretty reasonable. I only refactored it using either (from Data.Either) in order to supply a custom error message.
It is worth mentioning that liftM2 (or liftM3, etc.) code tends to look nicer if written using applicative style. For instance, the interesting case in the Point instance might become:
parseJSON (Object v) = Point <$> v .: "x" <*> v .: "y"
I just implemented solution for simple case. Here is the source code:
lookupE :: Value -> Text -> Either String Value
lookupE (Object obj) key = case H.lookup key obj of
Nothing -> Left $ "key " ++ show key ++ " not present"
Just v -> Right v
loopkupE _ _ = Left $ "not an object"
(.:*) :: (FromJSON a) => Value -> [Text] -> Parser a
(.:*) value = parseJSON <=< foldM ((either fail return .) . lookupE) value
instance FromJSON Graph where
parseJSON (Object v) = do
i <- parseJSON =<< v .: "id"
vs <- parseJSON =<< v .: "vertices"
sc <- parseJSON =<< v .: "scenario"
buildGraph i sc <$> concat <$> parseVertices vs
where
parseVertices v#(Object o) = parseFromTo minID maxID v
where
minID = unpackIndex $ Foldable.minimum ids
maxID = unpackIndex $ Foldable.maximum ids
unpackIndex eitherI = case eitherI of
Right i -> i
Left e -> error e
ids = map ((fmap fst) . TR.decimal) (M.keys o)
parseVertex i v = do
p1 <- v .:* [(T.pack $ show i), "x"]
p2 <- v .:* [(T.pack $ show i), "y"]
return $ vertex i p1 p2
parseFromTo i j v | i == j = return []
| otherwise = do
vertex <- parseVertex i v
liftM2 (:) (return [vertex]) (parseFromTo (i + 1) j v)
buildGraph :: Int -> Scenario -> [Vertex] -> Graph
buildGraph i sc vertices = Graph i vertices sc
parseJSON _ = mzero
Function lookupE and (.:*) are from Petr Pudlák's answer.
I don't really like this implementation of parseJSON function. But it works in cases when my vertices have ids with delta 1. I know that I could not extracted value from Foldable.minimum ids and Foldable.maximum ids, but it has brought me to the monad hell (a little one).
So here is an example of json-file, after parsing of which we got Nothing:
{
"id": 1,
"vertices": {
"3": {
"y": 12,
"x": 0
},
"2": {
"y": 16,
"x": 24
},
"1": {
"y": 12,
"x": 10
}
},
"scenario": [
1,
2,
3,
1
]
}
So I leave this question opened for now.
Update
Oh, I just saw my mistake. I already have all keys. :)
ids = map ((fmap fst) . TR.decimal) (M.keys o)
Now I leave this question opened for few days more. Maybe someone will improve my solution.
Update 2
Thanks to duplode, I made code more clear and readable.
Here is the source:
instance FromJSON Point where
parseJSON (Object v) = liftM2 Point (v .: "x") (v .: "y")
instance FromJSON [Vertex] where
parseJSON (Object o) = mapM parseVertex $ M.toList o
where
parseVertex (rawID, rawPoint) = Vertex (fromRight . (fmap fst) . TR.decimal $ rawID) <$> parseJSON rawPoint
instance FromJSON Graph where
parseJSON (Object v) = do
i <- parseJSON =<< v .: "id"
vs <- parseJSON =<< v .: "vertices"
sc <- parseJSON =<< v .: "scenario"
return $ Graph i vs sc
instance FromJSON [Graph] where
parseJSON (Object o) = mapM parseGraph $ M.toList o
where
parseGraph (_, rawGraph) = parseJSON rawGraph
And I don't need any helper functions to extract nested values.
BTW, I don't know any better way to create Vertex rather then Vertex (fromRight . (fmap fst) . TR.decimal $ rawID) <$> parseJSON rawPoint. liftM2 can't be used because second argument has type Either a b, but third has type Parser c. Can't combine :)

In Haskell, how do I decode a JSON value that could be of two different types?

I'm trying to parse some bibliographic data, more specifically, pull out the 'subject' field for each item. The data is json and looks something like this:
{"rows": [
{"doc":{"sourceResource": {"subject": ["fiction", "horror"]}}},
{"doc":{"sourceResource": {"subject": "fantasy"}}}
]}
I can pull out 'subject' if every entry is either Text or [Text], but I'm stumped as to how to accommodate both. Here is my program in its current state:
{-# LANGUAGE OverloadedStrings#-}
import Debug.Trace
import Data.Typeable
import Data.Aeson
import Data.Text
import Control.Applicative
import Control.Monad
import qualified Data.ByteString.Lazy as B
import Network.HTTP.Conduit (simpleHttp)
import qualified Data.HashMap.Strict as HM
import qualified Data.Map as Map
jsonFile :: FilePath
jsonFile = "bib.json"
getJSON :: IO B.ByteString
getJSON = B.readFile jsonFile
data Document = Document { rows :: [Row]}
deriving (Eq, Show)
data Row = SubjectList [Text]
| SubjectText Text
deriving (Eq, Show)
instance FromJSON Document where
parseJSON (Object o) = do
rows <- parseJSON =<< (o .: "rows")
return $ Document rows
parseJSON _ = mzero
instance FromJSON Row where
parseJSON (Object o) = do
item <- parseJSON =<< ((o .: "doc") >>=
(.: "sourceResource") >>=
(.: "subject"))
-- return $ SubjectText item
return $ SubjectList item
parseJSON _ = mzero
main :: IO ()
main = do
d <- (decode <$> getJSON) :: IO (Maybe Document)
print d
Any help would be appreciated.
Edit:
the working FromJSON Row instance:
instance FromJSON Row where
parseJSON (Object o) =
(SubjectList <$> (parseJSON =<< val)) <|>
(SubjectText <$> (parseJSON =<< val))
where
val = ((o .: "doc") >>=
(.: "sourceResource") >>=
(.: "subject"))
parseJSON _ = mzero
First, look at the type of
((o .: "doc") >>=
(.: "sourceResource") >>=
(.: "subject")) :: FromJSON b => Parser b
We can get out of it anything that's an instance of FromJSON. Now, clearly, this can work for Text or [Text] individually, but your problem is that you want to get either Text or [Text]. Fortunately, it should be fairly easy to deal with this. Rather than letting it decode it for you further, just get a Value out of it. Once you've got a Value, you could decode it as a Text and put it in a SubjectText:
SubjectText <$> parseJSON val :: Parser Row
Or as a [Text] and put it in a SubjectList:
SubjectList <$> parseJSON val :: Parser Row
But wait, either one of these will do, and they have the same output type. Notice that Parser is an instance of Alternative, which lets us say exactly that (“either one will do”). Thus,
(SubjectList <$> parseJSON val) <|> (SubjectText <$> parseJSON val) :: Parser Row
Ta-da! (Actually, it wasn't necessary to pull it out as a Value; we could have instead embedded that long ((o .: "doc") >>= (.: "sourceResource") >>= (.: "subject")) chain into each subexpression. But that's ugly.)

Haskell, Aeson - how to debug instances?

I have a complex nested json, which i'm trying to parse with Aeson and Attoparsec, into my custom types. Based on info from questions: Haskell, Aeson & JSON parsing into custom type, Aeson: How to convert Value into custom type? and some info from Internet.
When I'm using following code I'm getting "Nothing" Value from overlapped FromJSON instance, but code goes through each instance for sure, I've tested this by disabling some other insances. So the main question : how to test code in instances and see how data changes over execution in GHCi?
P.S: Tried to set breakpoints and "trace", but they are worked only in main & parseCfg functions.
{-# LANGUAGE OverloadedStrings, FlexibleInstances #-}
-- high level data
data Cfg = Cfg { nm :: CProperty,
author :: CProperty,
langs :: CValue,
grops :: CListArr,
projs :: CPropArr
} deriving (Show)
...
instance FromJSON CProperty where
parseJSON _ = mzero
parseJSON (Object o) = CProperty <$> toCProperty o
where
toCProperty :: (HM.HashMap T.Text Value) -> J.Parser (T.Text, T.Text)
toCProperty _ = error "unexpected property"
toCProperty o' = do
l <- return $ HM.toList o'
k <- return $ fst $ head l
v <- return $ snd $ head l
v' <- parseJSON v
return $ (k, v')
... lot's of different instances
-- |this instance is specific for different files
-- based on common functions to work with most of nested json code
instance FromJSON Cfg where
parseJSON _ = mzero
parseJSON (Object o) = do
nm <- (parseJSON :: Value -> J.Parser CProperty) =<< (o .: T.pack "Name")
autor <- (parseJSON :: Value -> J.Parser CValue) =<< (o .: T.pack "Author")
langs <- (parseJSON :: Value -> J.Parser CProperty) =<< (o .: T.pack "Languages")
groups <- (parseJSON :: Value -> J.Parser CListArr) =<< (o .: T.pack "Groups")
projs <- (parseJSON :: Value -> J.Parser CPropArr) =<< (o .: T.pack "Projects")
return $ Cfg nm author langs groups projs
------------------------------------------------------------------------------------
main :: IO ()
main = do:
s <- L.readFile "/home/config.json"
-- print $ show s
let cfg = parseCfg s
print $ show $ cfg
parseCfg :: L.ByteString -> Maybe Cfg
parseCfg s = decode s
The obvious problem is that in
instance FromJSON CProperty where
parseJSON _ = mzero
parseJSON (Object o) = ...
the first clause matches all input, so your instance returns mzero whatever the argument is. You should change the order of the clauses.
When compiling with warnings, GHC would tell you of the overlapping patterns.