使用Aeson的`genericToJSON`将产品类型编码为对象,而不是列表

时间:2018-03-29 18:45:53

标签: json haskell aeson

在以下代码中,encoded等于{"FooBar":[{"Bar":4},{"Baz":2}]}

所需的编码为{"FooBar":{"Bar":4,"Baz":2}}

import           Data.Aeson
import           Data.Aeson.Types

data Foo = FooBar Bar Baz deriving Generic

newtype Bar = Bar Integer deriving Generic

newtype Baz = Baz Integer deriving Generic

instance ToJSON Foo where
  toJSON = genericToJSON options

instance ToJSON Bar where
  toJSON = genericToJSON options

instance ToJSON Baz where
  toJSON = genericToJSON options

options :: Options
options = defaultOptions { tagSingleConstructors = True, 
                           sumEncoding = ObjectWithSingleField }

foo :: Foo
foo = FooBar (Bar 4) (Baz 2)

encoded = encode foo

也就是说,我希望产品类型的构造函数是键,它们的组件是对象。我与tagSingleConstructors = TruesumEncoding = ObjectWithSingleField非常接近,但是Aeson将BarBaz放入列表而不是对象。

Options中的其他修饰符似乎都不相关,但我可能会遗漏某些内容。 genericToJSON是否可以进行所需的编码?

1 个答案:

答案 0 :(得分:1)

使用记录语法可以实现期望的结果。 在这种情况下,Aeson会将值编码为对象。 但是,在这种情况下需要两组选项,一组用于FooBar,另一组用于FooBar

data Foo = FooBar { bar:: Bar, baz :: Baz} deriving Generic

data Bar = Bar Integer deriving Generic

newtype Baz = Baz Integer deriving Generic
instance ToJSON Foo where
  toJSON = genericToJSON options

instance ToJSON Bar where
  toJSON = genericToJSON option_nested

instance ToJSON Baz where
  toJSON = genericToJSON option_nested

options :: Options
options = defaultOptions { tagSingleConstructors = True,
                           sumEncoding = ObjectWithSingleField,
                           fieldLabelModifier = \s -> (toUpper . head) s : (tail s)  }
option_nested :: Options
option_nested = defaultOptions { sumEncoding = UntaggedValue }

结果:{"FooBar":{"Baz":2,"Bar":4}}"