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

启用Strict编译指示时Aeson 2跨模块导入字段异常求助

问题现象与解决方案

问题背景

在迁移至aeson 2.0.3.0(基于GHC 9.2.7,Stack LTS-20.13)时,出现跨模块导入记录类型的JSON编码错误:字段code被错误编码为body,且body字段重复出现。该问题仅在跨模块导入时触发,移除Strict编译指示后消失,在旧版aeson 1.4.7(GHC 8.8.4,Stack LTS-16.31)中无此问题。

复现代码

模块Test1.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell,DeriveGeneric #-}
{-# LANGUAGE Strict #-}

module Test1
where

import Data.Aeson.TH
import Data.Aeson
import GHC.Generics
import qualified Data.Text as T (Text)

data Rsp = Rsp { code::Int, tag :: T.Text, body:: T.Text } deriving (Show,Eq,Ord)
deriveJSON defaultOptions ''Rsp

GHCi错误输出

ghci> import Test1
ghci> import Data.Aeson (encode)
ghci> encode $ Rsp (1::Int) "nyi" ""
"{\"body\":1,\"tag\":\"nyi\",\"body\":\"\"}"

原因分析

该问题源于GHC 9.2+的Strict扩展与aeson 2.x的Template Haskell推导逻辑不兼容:

  • Strict扩展会将记录字段的严格性标记嵌入到字段的内部表示中;
  • aeson 2.x的TH推导代码在处理跨模块的严格记录时,错误地将严格性信息混淆为字段名的一部分,导致字段名映射错误(code被识别为body),最终出现重复字段。

旧版aeson(1.4.x)的TH推导逻辑未处理这种严格性标记的变化,且GHC 8.8.x的Strict扩展实现与9.2+不同,因此未触发该问题。

解决方案

1. 替换Strict为StrictData

StrictData仅作用于数据类型的字段严格性,不影响绑定的严格性,与aeson的TH推导兼容性更好:

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell,DeriveGeneric #-}
{-# LANGUAGE StrictData #-} -- 替换原Strict扩展

module Test1
where

-- 剩余代码不变

2. 显式标记字段严格性

如果需要保留局部严格性,移除Strict扩展,改用显式!标记单个字段的严格性:

data Rsp = Rsp { !code::Int, !tag :: T.Text, !body:: T.Text } deriving (Show,Eq,Ord)

3. 手动编写JSON实例

绕过Template Haskell,手动实现ToJSON/FromJSON实例,彻底避免兼容性问题:

instance ToJSON Rsp where
  toJSON (Rsp code tag body) = object
    [ "code" .= code
    , "tag" .= tag
    , "body" .= body
    ]

instance FromJSON Rsp where
  parseJSON = withObject "Rsp" $ \v -> Rsp
    <$> v .: "code"
    <*> v .: "tag"
    <*> v .: "body"

4. 升级aeson版本

检查是否有更新的aeson版本修复了该兼容性问题,后续版本可能已针对GHC 9.x的Strict扩展做了适配。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 18:27:49