Parsing RoseTree JSON in Haskell - json

I am trying to parse JSON representation of a RoseTree. Here is a snapshot I have:
module RoseTree2 where
import Data.Tree
import Data.Aeson
import qualified Data.Text as T
import Control.Applicative
data RoseTree2 = RoseNode Int [RoseTree2] deriving (Show)
instance ToJSON RoseTree2 where
toJSON (RoseNode n cs) =
object [T.pack "value" .= show n
, T.pack "children".= show cs]
instance FromJSON RoseTree2 where
parseJSON (Object o) =
RoseNode <$> o.: T.pack "value"
<*> o.: T.pack "children"
But I am getting following error on fileload:
RoseTree2.hs:10:10:
No instance for (GToJSON (GHC.Generics.Rep RoseTree2))
arising from a use of `aeson-0.7.0.6:Data.Aeson.Types.Class.$gdmtoJSON'
Possible fix:
add an instance declaration for
(GToJSON (GHC.Generics.Rep RoseTree2))
In the expression:
(aeson-0.7.0.6:Data.Aeson.Types.Class.$gdmtoJSON)
In an equation for `toJSON':
toJSON = (aeson-0.7.0.6:Data.Aeson.Types.Class.$gdmtoJSON)
In the instance declaration for `ToJSON RoseTree2'
Failed, modules loaded: none.
Could please someone tell me what's wrong with my definition of a JSON parser and how could I fix it? Thanks!

You need to indent the definition of toJSON
instance ToJSON RoseTree2 where
toJSON (RoseNode n cs) =
object [T.pack "value" .= show n
, T.pack "children".= show cs]

You forgot to indent the line after instance ToJSON RoseTree2 so the instance block is closed, and it defaults to
default toJSON :: (Generic a, GToJSON (Rep a)) => a -> Value
toJSON = genericToJSON defaultOptions

Related

how to parse json with field of optional and variant type in Haskell?

How I can parse the input json inside this file ? https://github.com/smogon/pokemon-showdown/blob/master/data/moves.js
For the secondary and flags properties? They are optional and contains variant type.
A minimal example would be this one:
[
{},
{
"secondary": false
},
{
"secondary": {
"chance": 10,
"boosts": {
"spd": -1
}
}
},
{
"secondary": {
"chance": 30,
"volatileStatus": "flinch"
}
},
{
"secondary": {
"chance": 30
}
},
{
"secondary": {
"chance": 10,
"self": {
"boosts": {
"atk": 1,
"def": 1,
"spa": 1,
"spd": 1,
"spe": 1
}
}
}
},
{
"secondary": {
"chance": 10,
"status": "brn"
}
},
{
"secondary": {
"chance": 50,
"self": {
"boosts": {
"def": 2
}
}
}
},
{
"secondary": {
"chance": 100,
"self": {}
}
},
{
"secondary": {
"chance": 50,
"boosts": {
"accuracy": -1
}
}
}
]
For your convenience, you can choose to attach this snippet to the end of the js file and run it using node move.js. Two valid json files will be saved to your disk. One is a list of json objects while the other is an object with string as key.
var fs = require('fs');
fs.writeFile("moves_object.json", JSON.stringify(BattleMovedex), function(err) {}); // 1. save a json object with string key
var jsonList = []
for (var key of Object.keys(BattleMovedex)) {
jsonList.push(BattleMovedex[key]);
}
fs.writeFile("moves.json", JSON.stringify(jsonList), function(err) { // 2. save as a list of json object
if (err) {
console.log(err);
}
});
FYI:
If you are familiar with c++, you might find it easier to understand the same problem in this post:
How to parse json file with std::optional< std::variant > type in C++?
NOTE: In the code examples below, I've used a "moves.json" file whose contents are your minimal example above. Except for getMoves, which can parse any valid JSON, the other code examples won't work on the "moves.json" file derived from the linked "moves.js" file because the format is different (e.g., it's an object, not an array, for one thing).
The simplest way of using Aeson to parse arbitrary JSON is to parse it to a Value:
import Data.Aeson
import Data.Maybe
import qualified Data.ByteString.Lazy as B
getMoves :: IO Value
getMoves = do
mv <- decode <$> B.readFile "moves.json"
case mv of
Nothing -> error "invalid JSON"
Just v -> return v
Any valid JSON can be parsed this way, and the resulting Value has completely dynamic structure that can be programmatically inspected at runtime. The Lens library and Maybe monad can be helpful here. For example, to find the (first) object with a non-missing secondary.chance of 100, you could use:
{-# LANGUAGE OverloadedStrings #-}
import Control.Lens
import Data.Aeson
import Data.Aeson.Lens
import qualified Data.Vector as Vector
import qualified Data.ByteString.Lazy as B
find100 :: Value -> Maybe Value
find100 inp = do
arr <- inp ^? _Array
Vector.find (\s -> s ^? key "secondary" . key "chance" . _Integer == Just 100) arr
test1 = find100 <$> getMoves
which outputs:
> test1
Just (Object (fromList [("secondary",Object (fromList [("chance",Number 100.0),
("self",Object (fromList []))]))]))
which is the Value representation of the object:
{
"secondary": {
"chance": 100,
"self": {}
}
}
If you want the resulting parsed object to have more structure, then you need to start by figuring out a Haskell representation that will work with all possible objects you're planning to parse. For your example, a reasonable representation might be:
type Moves = [Move]
data Move = Move
{ secondary :: Secondary'
} deriving (Show, Generic)
newtype Secondary' = Secondary' (Maybe Secondary) -- Nothing if json is "false"
deriving (Show, Generic)
data Secondary = Secondary
{ chance :: Maybe Int
, boosts :: Maybe Boosts
, volatileStatus :: Maybe String
, self :: Maybe Self
} deriving (Show, Generic)
data Self = Self
{ boosts :: Maybe Boosts
} deriving (Show, Generic)
newtype Boosts = Boosts (HashMap.HashMap Text.Text Int)
deriving (Show, Generic)
This assumes that all moves have a secondary field which is either "false" or an object. It also assumes that lots of boost keys are possible, so it's more convenient to represent them as arbitrary text strings in a Boosts hashmap. Also, this handles having the "boosts" directly under "secondary" or nested within "self", since your example included examples of both forms, though maybe this was a mistake.
For these data types, the default instances for Move, Self, and Secondary can all be used:
instance FromJSON Move
instance FromJSON Self
instance FromJSON Secondary
The Secondary' newtype wrapper around Secondary is then used to handle false versus an object using a custom instance:
instance FromJSON Secondary' where
parseJSON (Bool False) = pure $ Secondary' Nothing
parseJSON o = Secondary' . Just <$> parseJSON o
A custom instance is also needed for Boosts to parse it into the appropriate hashmap:
instance FromJSON Boosts where
parseJSON = withObject "Boosts" $ \o -> Boosts <$> mapM parseJSON o
Now, with the following driver:
test2 :: IO (Either String Moves)
test2 = eitherDecode <$> B.readFile "moves.json"
this decodes your example like so:
> test2
Right [Move {secondary = Secondary' Nothing},Move {secondary =
Secondary' (Just (Secondary {chance = Just 10, boosts = Just (Boosts
(fromList [("spd",-1)])), volatileStatus = Nothing, self =
...
By using eitherDecode above, we can get an error message if the parse fails. For example, if you run this on the "moves.json" derived from "moves.js" instead, you get:
> test2
Left "Error in $: parsing [] failed, expected Array, but encountered Object"
when the parser notices that it's trying to parse a [Move] array but is instead finding an object keyed by Pokemon move names.
Here's the full code showing both types of parsing:
{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE OverloadedStrings #-}
import Control.Lens
import Data.Aeson
import Data.Aeson.Lens
import GHC.Generics
import qualified Data.Text as Text
import qualified Data.HashMap.Strict as HashMap
import qualified Data.Vector as Vector
import qualified Data.ByteString.Lazy as B
--
-- Parse into a dynamic Value representation
getMoves :: IO Value
getMoves = do
mv <- decode <$> B.readFile "moves.json"
case mv of
Nothing -> error "invalid JSON"
Just v -> return v
find100 :: Value -> Maybe Value
find100 inp = do
arr <- inp ^? _Array
Vector.find (\s -> s ^? key "secondary" . key "chance" . _Integer == Just 100) arr
test1 :: IO (Maybe Value)
test1 = find100 <$> getMoves
--
-- Parse into suitable static data structures
-- whole file is array of moves
type Moves = [Move]
data Move = Move
{ secondary :: Secondary'
} deriving (Show, Generic)
newtype Secondary' = Secondary' (Maybe Secondary) -- Nothing if json is "false"
deriving (Show, Generic)
data Secondary = Secondary
{ chance :: Maybe Int
, boosts :: Maybe Boosts
, volatileStatus :: Maybe String
, self :: Maybe Self
} deriving (Show, Generic)
data Self = Self
{ boosts :: Maybe Boosts
} deriving (Show, Generic)
newtype Boosts = Boosts (HashMap.HashMap Text.Text Int)
deriving (Show, Generic)
instance FromJSON Move
instance FromJSON Self
instance FromJSON Secondary
instance FromJSON Secondary' where
parseJSON (Bool False) = pure $ Secondary' Nothing
parseJSON o = Secondary' . Just <$> parseJSON o
instance FromJSON Boosts where
parseJSON = withObject "Boosts" $ \o -> Boosts <$> mapM parseJSON o
test2 :: IO (Either String Moves)
test2 = eitherDecode <$> B.readFile "moves.json"
here is another attempt to your mover.json
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DuplicateRecordFields #-}
module Main where
import Control.Applicative
import Data.Maybe
import Data.Text (Text)
import GHC.Generics
import Data.Aeson
main :: IO ()
main = do
result <- eitherDecodeFileStrict "/tmp/helloworld/movers.json"
case ( result :: Either String [Move]) of
Left error -> print error
Right ms -> print (length ms)
data Move = Move
{ num :: Int
, accuracy :: Either Int Bool
, secondary :: Maybe (Either Bool Secondary)
} deriving (Generic, Show)
data Secondary = Secondary
{ chance :: Maybe Int
, volatileStatus :: Maybe Text
, boosts :: Maybe Boosts
, self :: Maybe Self
, status :: Maybe Text
} deriving (Generic, Show)
data Boosts = Boosts
{ atk :: Maybe Int
, def :: Maybe Int
, spa :: Maybe Int
, spd :: Maybe Int
, spe :: Maybe Int
} deriving (Generic, Show)
data Self = Self
{ boosts :: Maybe Boosts
} deriving (Generic, Show)
instance FromJSON Move where
parseJSON (Object v) = Move
<$> v .: "num"
<*> ( (Left <$> v .: "accuracy")
<|> (Right <$> v .: "accuracy")
)
<*> ( fmap (fmap Left) (v .:? "secondary")
<|> fmap (fmap Right) (v .:? "secondary")
)
instance FromJSON Secondary
instance FromJSON Boosts
instance FromJSON Self
My attempt to the minimal sample
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DuplicateRecordFields #-}
module Main where
import Data.Text
import GHC.Generics
import Data.Aeson
main :: IO ()
main = do
result <- eitherDecodeFileStrict "/tmp/helloworld/minimal.json"
print (result :: Either String [Foo])
data Foo = Foo { secondary :: Either Bool Bar } deriving (Generic, Show)
data Bar = Chance
{ chance :: Int
, volatileStatus :: Maybe Text
, boosts :: Maybe Boosts
, self :: Maybe Self
, status :: Maybe Text
} deriving (Generic, Show)
data Boosts = Boosts
{ atk :: Maybe Int
, def :: Maybe Int
, spa :: Maybe Int
, spd :: Maybe Int
, spe :: Maybe Int
} deriving (Generic, Show)
data Self = Self
{ boosts :: Maybe Boosts
} deriving (Generic, Show)
instance FromJSON Foo where
parseJSON (Object v) = do
sd <- v .: "secondary" -- Parse Value
case sd of
Bool x -> return . Foo . Left $ x
otherwise -> (Foo . Right) <$> parseJSON sd
instance FromJSON Bar
instance FromJSON Boosts
instance FromJSON Self

Haskell Aeson json encoding bytestrings

I need to serialize a record in Haskell, and am trying to do it with Aeson. The problem is that some of the fields are ByteStrings, and I can't work out from the examples how to encode them. My idea is to first convert them to text via base64. Here is what I have so far (I put 'undefined' where I didn't know what to do):
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
module Main where
import qualified Data.Aeson as J
import qualified Data.ByteString as B
import qualified Data.ByteString.Base64 as B64
import qualified Data.Text as T
import qualified Data.Text.Encoding as E
import qualified GHC.Generics as G
data Data = Data
{ number :: Int
, bytestring :: B.ByteString
} deriving (G.Generic, Show)
instance J.ToJSON Data where
toEncoding = J.genericToEncoding J.defaultOptions
instance J.FromJSON Data
instance J.FromJSON B.ByteString where
parseJSON = undefined
instance J.ToJSON B.ByteString where
toJSON = undefined
byteStringToText :: B.ByteString -> T.Text
byteStringToText = E.decodeUtf8 . B64.encode
textToByteString :: T.Text -> B.ByteString
textToByteString txt =
case B64.decode . E.encodeUtf8 $ txt of
Left err -> error err
Right bs -> bs
encodeDecode :: Data -> Maybe Data
encodeDecode = J.decode . J.encode
main :: IO ()
main = print $ encodeDecode $ Data 1 "A bytestring"
It would be good if it was not necessary to manually define new instances of ToJSON and FromJSON for every record, because I have quite a few different records with bytestrings in them.
parseJson needs to return a value of type Parser B.ByteString, so you just need to call pure on the return value of B64.decode.
import Control.Monad
-- Generalized to any MonadPlus instance, not just Either String
textToByteString :: MonadPlus m => T.Text -> m B.ByteString
textToByteString = case B64.decode (E.encodeUtf8 x) of
Left _ -> mzero
Right bs -> pure bs
instance J.FromJSON B.ByteString where
parseJSON (J.String x) = textToByteString x
parseJSON _ = mzero
Here, I've chosen to return mzero both if you try to decode anything other than a JSON string and if there is a problem with the base-64 decoding.
Likewise, toJSON needs just needs to encode the Text value you create from the base64-encoded ByteString.
instance J.ToJSON B.ByteString where
toJSON = J.toJSON . byteStringToText
You might want to consider using a newtype wrapper instead of defining the ToJSON and FromJSON instances on B.ByteString directly.

Parsing JSON into RoseTree (recursive parsing)

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?
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 = []}]})

Automatically deriving instance for custom data type with Aeson/JSON

If I have a custom data type for parsing JSON with Aeson
data Response = Response
{ response :: [Body]
} deriving (Show)
instance FromJSON Response where
parseJSON (Object v) = Response <$> v .: "response"
parseJSON _ = mzero
data Body = Body
{ body_id :: Int
, brandId :: Int
} deriving (Show)
instance FromJSON Body where
parseJSON (Object v) = Body
<$> v .: "id"
<*> v .: "brandId"
parseJSON _ = mzero
raw :: BS.ByteString
raw = "{\"response\":[{\"id\":5977,\"brandId\":87}]}"
giving:
λ> decode raw :: Maybe Response
Just (Response {response = [Body {body_id = 5977, brandId = 87}]})
How do I derive the instances for FromJSON automatically?
I have tried:
data Response = Response
{ response :: [Body]
} deriving (Show,Generic)
data Body = Body
{ body_id :: Int
, brandId :: Int
} deriving (Show,Generic)
instance FromJSON Response
instance FromJSON Body
as has been suggested from some tutorials, but that gives:
λ> :l response.hs
[1 of 1] Compiling Response ( response.hs, interpreted )
response.hs:19:22:
Can't make a derived instance of `Generic Response':
You need DeriveGeneric to derive an instance for this class
In the data declaration for `Response'
response.hs:24:22:
Can't make a derived instance of `Generic Body':
You need DeriveGeneric to derive an instance for this class
In the data declaration for `Body'
Failed, modules loaded: none.
what am I doing wrong?
What the error is telling you is that you have to enable the DeriveGeneric extension in order for this to work. So you have to add:
{-# LANGUAGE DeriveGeneric #-}
right at the top of your file, or compile using the -XDeriveGeneric flag.

Haskell, Aeson & JSON parsing into custom type

Following on from a previous post, I've found I'm totally stuck. I'm trying to parse a JSON structure into my own type, and not only am I stuck on how to parse the Array, I'm not even sure if I'm using the Aeson library as intended. Any help would be greatly appreciated.
The code:
data Exif = Exif [(T.Text, ExifValue)] deriving (Show)
data ExifValue =
ExifText T.Text |
ExifInt Integer |
ExifDouble Double |
ExifBool Bool |
ExifArray [ExifValue]
deriving (Show)
instance FromJSON ExifValue where
parseJSON (Number (I n)) = return $ ExifInt n
parseJSON (Number (D n)) = return $ ExifDouble n
parseJSON (String s) = return $ ExifText s
parseJSON (Bool b) = return $ ExifBool b
-- parseJSON (Array a) = ?????
instance FromJSON Exif where
parseJSON (Object o) = do
x <- sequence $ map f (M.assocs o)
return $ Exif x
where
f (t, x) = do
y <- parseJSON x
return ((t, y) :: (T.Text, ExifValue))
parseExifFile = fmap parseExifData . B.readFile
parseExifData :: B.ByteString -> Data.Attoparsec.Result (Data.Aeson.Result [Exif])
parseExifData content = parse (fmap fromJSON json) content
The test file:
[{
"SourceFile": "test.jpg",
"ExifTool:ExifToolVersion": 8.61,
"File:FileName": "test.jpg",
"File:FileSize": 2174179,
"File:FileModifyDate": "2011:07:27 16:53:49-07:00",
"File:FilePermissions": 644,
"File:FileType": "JPEG",
"File:MIMEType": "image/jpeg",
"File:ExifByteOrder": "MM",
"File:CurrentIPTCDigest": "32d6a77098a73aa816f2570c9472735a",
"File:ImageWidth": 2592,
"File:ImageHeight": 1936,
"File:EncodingProcess": 0,
"File:BitsPerSample": 8,
"File:ColorComponents": 3,
"File:YCbCrSubSampling": "2 2",
"XMP:Subject": ["alpha","beta","gamma"]
}]
You have to follow the type of parseJSON a little bit down a rabbit trail, but once you recognize what (Array a) represents, it should be straightforward.
parseJSON has type Value -> Parser a, so (Array a) has type Value. One of the variants in the Value type is Array Array, so the a in (Array a) must be of the type Array, which is defined as Vector Value. The Values inside that Vector are what you want to call parseJSON on to return your list, so check out what you can do with a Vector.
The easiest approach would probably to convert a to a list with Vector.toList, and then use mapM to parse the Values.
Alternately, you could avoid the Vector to list conversion by changing your ExifArray variant to hold Vector ExifValue, and then using Vector.mapM.
I'm not native english speaker, so i may not understand you very well. I guess you want to know how to parse json into recursive data type like ExifValue you presented.
So i made a simple example to show how to parse json into recursive data type.
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.ByteString as B
import Data.Maybe
import Control.Monad
import Control.Applicative
import Data.Attoparsec
import Data.Attoparsec.Number
import Data.Aeson
import qualified Data.Vector as V
data Data = D1 Int | D2 [Data]
deriving (Show)
instance FromJSON Data where
parseJSON (Number (I n)) = return $ D1 $ fromInteger n
parseJSON (Array a) = D2 <$> mapM parseJSON (V.toList a)
main = do
let v = fromJust $ maybeResult $ parse json "[1,2,3,[5,3,[6,3,5]]]"
let v1 :: Data
v1 = case fromJSON v of
Success a -> a
Error s -> error s
print v1
A slightly newer build of the aeson library (0.3.2.12) supports autogenerating JSON instances.
{-# LANGUAGE TemplateHaskell #-}
import Data.Aeson
import Data.Aeson.TH (deriveJSON)
import Data.Attoparsec
import qualified Data.ByteString as B
import qualified Data.Text as T
data Exif = Exif [(T.Text, ExifValue)] deriving (Show)
data ExifValue =
ExifText T.Text |
ExifInt Integer |
ExifDouble Double |
ExifBool Bool |
ExifArray [ExifValue]
deriving (Show)
deriveJSON id ''Exif
deriveJSON id ''ExifValue
parseExifFile = fmap parseExifData . B.readFile
parseExifData :: B.ByteString -> Data.Attoparsec.Result (Data.Aeson.Result [Exif])
parseExifData content = parse (fmap fromJSON json) content
Produces:
instance ToJSON Exif where
{ toJSON
= \ value_a1Va
-> case value_a1Va of { Exif arg1_a1Vb -> toJSON arg1_a1Vb } }
instance FromJSON Exif where
{ parseJSON
= \ value_a1Vc
-> case value_a1Vc of {
arg_a1Vd -> (Exif Data.Functor.<$> parseJSON arg_a1Vd) } }
instance ToJSON ExifValue where
{ toJSON
= \ value_a1Wd
-> case value_a1Wd of {
ExifText arg1_a1We
-> object [(T.pack "ExifText" .= toJSON arg1_a1We)]
ExifInt arg1_a1Wf
-> object [(T.pack "ExifInt" .= toJSON arg1_a1Wf)]
ExifDouble arg1_a1Wg
-> object [(T.pack "ExifDouble" .= toJSON arg1_a1Wg)]
ExifBool arg1_a1Wh
-> object [(T.pack "ExifBool" .= toJSON arg1_a1Wh)]
ExifArray arg1_a1Wi
-> object [(T.pack "ExifArray" .= toJSON arg1_a1Wi)] } }
instance FromJSON ExifValue where
{ parseJSON
= \ value_a1Wj
-> case value_a1Wj of {
Object obj_a1Wk
-> case Data.Map.toList obj_a1Wk of {
[(conKey_a1Wl, conVal_a1Wm)]
-> case conKey_a1Wl of {
_ | (conKey_a1Wl == T.pack "ExifText")
-> case conVal_a1Wm of {
arg_a1Wn
-> (ExifText Data.Functor.<$> parseJSON arg_a1Wn) }
| (conKey_a1Wl == T.pack "ExifInt")
-> case conVal_a1Wm of {
arg_a1Wo
-> (ExifInt Data.Functor.<$> parseJSON arg_a1Wo) }
| (conKey_a1Wl == T.pack "ExifDouble")
-> case conVal_a1Wm of {
arg_a1Wp
-> (ExifDouble Data.Functor.<$> parseJSON arg_a1Wp) }
| (conKey_a1Wl == T.pack "ExifBool")
-> case conVal_a1Wm of {
arg_a1Wq
-> (ExifBool Data.Functor.<$> parseJSON arg_a1Wq) }
| (conKey_a1Wl == T.pack "ExifArray")
-> case conVal_a1Wm of {
arg_a1Wr
-> (ExifArray Data.Functor.<$> parseJSON arg_a1Wr) }
| otherwise -> Control.Monad.mzero }
_ -> Control.Monad.mzero }
_ -> Control.Monad.mzero } }