Assume there is a data type
data V = V { a :: Int, x :: Int, y :: Int }
It has a correspondent JSON view
E.g. V { a = 1, x = 2, y = 3 }
need to be serialised like
{
"a": 1,
"nested": {
"x": 2,
"y": 3
}
}
What ToJSON
instance would look like in that case?
What I've tried:
instance ToJSON V where
toEncoding (V a b c) =
pairs ( "a" .= a
<> ("nested" .= pairs ("x" .= x <> "y" .= y))
)
<interactive>:6:10: error:
• No instance for (GHC.Generics.Generic V)
arising from a use of ‘aeson-1.1.1.0:Data.Aeson.Types.ToJSON.$dmtoJSON’
• In the expression:
aeson-1.1.1.0:Data.Aeson.Types.ToJSON.$dmtoJSON @V
In an equation for ‘toJSON’:
toJSON = aeson-1.1.1.0:Data.Aeson.Types.ToJSON.$dmtoJSON @V
In the instance declaration for ‘ToJSON V’
<interactive>:6:68: error:
• No instance for (ToJSON Encoding) arising from a use of ‘.=’
• In the second argument of ‘(<>)’, namely
‘("nested" .= pairs ("x" .= x <> "y" .= y))’
In the first argument of ‘pairs’, namely
‘("a" .= a <> ("nested" .= pairs ("x" .= x <> "y" .= y)))’
In the expression:
pairs ("a" .= a <> ("nested" .= pairs ("x" .= x <> "y" .= y)))
<interactive>:6:87: error:
• No instance for (ToJSON (V -> Int)) arising from a use of ‘.=’
(maybe you haven't applied a function to enough arguments?)
• In the first argument of ‘(<>)’, namely ‘"x" .= x’
In the first argument of ‘pairs’, namely ‘("x" .= x <> "y" .= y)’
In the second argument of ‘(.=)’, namely
‘pairs ("x" .= x <> "y" .= y)’
(0.01 secs,)
Here how instance may look like:
data V = V { a :: Int, x :: Int, y :: Int }
instance ToJSON V where
toJSON (V a x y) = object
[ "a" .= a
, "nested" .= object
[ "x" .= x
, "y" .= y ]
]
You can test it in ghci
:
ghci> import qualified Data.ByteString.Lazy.Char8 as B
ghci> B.putStrLn $ encode (V 1 2 3)
{"nested":{"x":2,"y":3},"a":1}
UPD (regarding toEncoding
):
You most likely don't want to define toEncoding
. This method has default implementation and it's defined using toJSON
method. But toJSON
method has no implementation for general case. It has only default
implementation for Generic
data types.
Your implementation if almost fine except it has typo: "x" .= x <> "y" .= y
in method body and (V a b c)
in pattern matching (thus it uses x
variable as function and you got those creepy errors). And you need to derive Generic
for your V
data type for this to work. And you need to use pair
function from internals instead of .=
in one place. Here is full version:
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveGeneric #-}
import Data.Monoid ((<>))
import GHC.Generics (Generic)
import Data.Aeson (ToJSON (..), pairs, (.=))
import Data.Aeson.Encoding.Internal (pair)
data V = V { a :: Int, x :: Int, y :: Int } deriving (Generic)
instance ToJSON V where
toEncoding (V a x y) =
pairs ("a" .= a <> (pair "nested" $ pairs ("x" .= x <> "y" .= y)))
But be aware of possible inconsistency:
ghci> encode (V 1 2 3)
"{\"a\":1,\"nested\":{\"x\":2,\"y\":3}}"
ghci> toEncoding (V 1 2 3)
"{\"a\":1,\"nested\":{\"x\":2,\"y\":3}}"
ghci> toJSON (V 1 2 3)
Object (fromList [("a",Number 1.0),("x",Number 2.0),("y",Number 3.0)])