You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Aeson的genericToJSON将产品类型编码为对象而非列表?

Fixing Aeson Generic Encoding: Array to Merged Object

Got it, let's tackle this encoding issue with Aeson's genericToJSON. The problem right now is that your Foo type is getting encoded with an array of two objects under the FooBar key, but you want those two objects merged into a single one. Let's break down why this is happening and how to fix it.

Why You're Getting the Array Output

Your Bar and Baz newtypes are being encoded as standalone objects (I'm guessing your options has unwrapUnaryRecords = False, which prevents Aeson from unwrapping the newtype to a raw Integer). By default, Aeson's generic encoding for a single-constructor product type (like Foo) puts all constructor parameters into an array. Combine those two behaviors, and you end up with an array of separate objects instead of one merged object.

The cleanest approach is to adjust your Foo type to use named record fields. This plays nicely with Aeson's generic encoder and makes your code more readable too.

First, update the Foo definition:

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

Next, tweak your options to ensure the JSON field names match your desired Bar and Baz (capitalized):

import Data.Char (toUpper)

-- Helper to capitalize the first letter of field names
capitalize :: String -> String
capitalize [] = []
capitalize (c:cs) = toUpper c : cs

options :: Options
options = defaultOptions
  { unwrapUnaryRecords = False  -- Keeps Bar/Baz as objects instead of raw Integers
  , fieldLabelModifier = capitalize  -- Turns lowercase "bar" into "Bar", "baz" into "Baz"
  }

With this setup, genericToJSON options will encode your Foo exactly how you want:

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

Solution 2: Manual Encoding (No Type Changes)

If you can't or don't want to modify the Foo type definition, you can manually merge the JSON objects from Bar and Baz in the ToJSON instance:

instance ToJSON Foo where
  toJSON (FooBar bar baz) = object
    [ "FooBar" .= mergeObjects (toJSON bar) (toJSON baz) ]
    where
      -- Merge two JSON Objects (safe since we know Bar/Baz encode to Objects)
      mergeObjects (Object o1) (Object o2) = Object (o1 <> o2)
      mergeObjects _ _ = Object mempty  -- Fallback for unexpected cases

This uses Aeson's Object monoid instance to combine the key-value pairs from Bar and Baz into a single object, eliminating the array entirely.

内容的提问来源于stack exchange,提问作者Matthew Piziak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:06:38