启用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
相关产品推荐
相关产品推荐

