JSON嵌套序列化

时间:2017-03-19 14:36:58

标签: json haskell aeson

假设有数据类型

data V = V { a :: Int, x :: Int, y :: Int }

它有一个相应的JSON视图

E.g。 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,)

1 个答案:

答案 0 :(得分:1)

这里的实例可能如下:

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方法对于一般情况没有实现。 default数据类型只有Generic个实现。

你的实现几乎没有问题,除非它在方法体中有错字:"x" .= x <> "y" .= y和模式匹配中的(V a b c)(因此它使用x变量作为函数,你得到了那些令人毛骨悚然的错误)。您需要为Generic数据类型派生V才能使其生效。而且您需要在一个地方使用内部而不是.=的{​​{3}}函数。这是完整版:

{-# 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)])