JSON 嵌套序列化
JSON nested serialisation
假设有一个数据类型
data V = V { a :: Int, x :: Int, y :: Int }
有通讯员JSON观点
例如V { a = 1, x = 2, y = 3 }
需要像
一样序列化
{
"a": 1,
"nested": {
"x": 2,
"y": 3
}
}
在那种情况下 ToJSON
实例会是什么样子?
我尝试过的:
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,)
实例可能如下所示:
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 ]
]
你可以在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(关于toEncoding
):
您很可能不想定义 toEncoding
。此方法具有默认实现,它是使用 toJSON
方法定义的。但是 toJSON
方法没有针对一般情况的实现。它只有 Generic
数据类型的 default
实现。
你的实现几乎没问题,除了它有拼写错误:方法体中的 "x" .= x <> "y" .= y
和模式匹配中的 (V a b c)
(因此它使用 x
变量作为函数,你得到了那些令人毛骨悚然的错误).您需要为您的 V
数据类型导出 Generic
才能工作。并且您需要在一个地方使用内部的 pair
函数而不是 .=
。这是完整版:
{-# 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)))
但要注意可能的不一致:
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)])
假设有一个数据类型
data V = V { a :: Int, x :: Int, y :: Int }
有通讯员JSON观点
例如V { a = 1, x = 2, y = 3 }
需要像
{
"a": 1,
"nested": {
"x": 2,
"y": 3
}
}
在那种情况下 ToJSON
实例会是什么样子?
我尝试过的:
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,)
实例可能如下所示:
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 ]
]
你可以在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(关于toEncoding
):
您很可能不想定义 toEncoding
。此方法具有默认实现,它是使用 toJSON
方法定义的。但是 toJSON
方法没有针对一般情况的实现。它只有 Generic
数据类型的 default
实现。
你的实现几乎没问题,除了它有拼写错误:方法体中的 "x" .= x <> "y" .= y
和模式匹配中的 (V a b c)
(因此它使用 x
变量作为函数,你得到了那些令人毛骨悚然的错误).您需要为您的 V
数据类型导出 Generic
才能工作。并且您需要在一个地方使用内部的 pair
函数而不是 .=
。这是完整版:
{-# 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)))
但要注意可能的不一致:
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)])