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

Haskell中用Aeson.TH为求和类型Declaration派生JSON序列化/反序列化器

没问题,我来一步步教你用Data.Aeson.TH给你的Declaration求和类型派生JSON序列化和反序列化实例,从基础用法到常见定制需求都给你讲清楚:

1. 先搞定依赖与导入

首先确保你的项目依赖里有aeson和template-haskell包(在cabal或stack配置里加上),然后在代码里开启模板Haskell扩展并导入必要模块:

{-# LANGUAGE TemplateHaskell #-}

import Data.Aeson.TH
import Data.Maybe (Maybe)
-- 别忘了导入你定义的 Constructor、Arg、Typ 类型,不然派生会报错!
2. 基础派生写法(快速上手)

最直接的方式就是用deriveJSON函数搭配默认配置,它会自动为求和类型生成带标识tag的JSON结构——每个构造器的JSON会包含一个tag字段(值为构造器名,比如"IndDecl"),再加上对应的记录字段:

deriveJSON defaultOptions ''Declaration

举个例子,IndDecl构造器生成的JSON大概长这样:

{
  "tag": "IndDecl",
  "what": "inductive",
  "name": "Nat",
  "argnames": ["a"],
  "constructors": []
}
3. 定制JSON结构(按需调整)

默认生成的结构可能不符合你的需求,比如想改tag字段名、调整字段格式、省略空值等,这时候可以通过修改Options来定制:

3.1 自定义构造器的tag字段名

比如不想用默认的"tag",改成"decl_type":

deriveJSON defaultOptions { constructorTagModifier = const "decl_type" } ''Declaration

如果想把构造器名改成小写(比如"inddecl"),可以用字符转换函数:

import Data.Char (toLower)

deriveJSON defaultOptions { constructorTagModifier = map toLower } ''Declaration

3.2 调整记录字段名

如果你的字段名需要格式转换(比如从驼峰式改下划线,或者反过来),用fieldLabelModifier。比如把字段名改成全小写(不过你的字段已经是小写了,这里举个通用例子):

-- 假设字段名是驼峰式,比如 argNames,转成 argnames
deriveJSON defaultOptions { fieldLabelModifier = map toLower } ''Declaration

3.3 省略空值字段

默认情况下,Maybe类型的字段如果是Nothing,会输出null。如果想直接省略这些字段,设置omitNothingFields = True:

deriveJSON defaultOptions { omitNothingFields = True } ''Declaration

比如argnames为Nothing时,JSON里就不会出现这个字段,更简洁。

3.4 自定义构造器的tag值

如果不想用构造器原名作为tag值(比如把IndDecl改成"inductive"),可以写个匹配函数:

deriveJSON defaultOptions { 
    constructorTagModifier = \case
        "IndDecl" -> "inductive"
        "TypeDecl" -> "type_def"
        "FixDecl" -> "fixity_rule"
        "TermDecl" -> "term_def"
        other -> other -- 其他构造器保持原名
} ''Declaration
4. 处理嵌套类型

你的Declaration里用到了Constructor、Arg、Typ这些类型,必须给它们也派生JSON实例,否则deriveJSON会报错。比如:

-- 假设你的 Constructor 是这样的记录类型
data Constructor = Constructor { conName :: String, conArgs :: [Arg] } deriving (Show, Eq)
deriveJSON defaultOptions ''Constructor

-- 同理处理 Arg 和 Typ
data Arg = Arg String deriving (Show, Eq)
deriveJSON defaultOptions ''Arg

data Typ = Typ String deriving (Show, Eq)
deriveJSON defaultOptions ''Typ
5. 测试序列化/反序列化

写个小测试验证一下是否正常工作:

import Data.Aeson (encode, decode)
import qualified Data.ByteString.Lazy.Char8 as BSL

testDecl :: Declaration
testDecl = IndDecl {
    what = "inductive",
    name = "Nat",
    argnames = Just ["n"],
    constructors = [Constructor "Zero" [], Constructor "Succ" [Arg "n"]]
}

main :: IO ()
main = do
    let jsonOutput = encode testDecl
    BSL.putStrLn jsonOutput -- 打印序列化后的JSON
    case decode jsonOutput of
        Just parsedDecl -> print $ parsedDecl == testDecl -- 应该输出 True
        Nothing -> putStrLn "反序列化失败!"
常见问题排查
  • 报错“找不到类型 Constructor/Arg/Typ”:检查这些类型是否已经定义,并且在当前模块可见(导入或同模块定义)。
  • 反序列化失败:核对JSON结构是否和派生的实例匹配——比如tag字段名是否正确、字段名大小写是否一致、嵌套类型的JSON格式是否正确。
  • 字段名不匹配:检查fieldLabelModifier的设置,有没有格式转换错误(比如大小写、下划线处理)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:38:23