JSON 转 Scala case class:省掉手写 DTO 的那半小时
把上游 API 的 JSON 样本粘进来直接拿 Scala case class,嵌套对象抽子类,null 包成 Option,数组转 List,Spark 和 Akka 后端接数据时不用再逐字段手敲类型。
JSON 转 Scala case class:省掉手写 DTO 的那半小时
接后端数据这件事,真正费时间的不是写逻辑,是把一坨 JSON 翻译成一组类型齐全的 Scala case class。一个上游 user 对象 25 个字段、散在三层嵌套里,手敲完还得逐个核对哪些是 Int 哪些是 Long、哪个键可能是 null。我用过的做法是把 JSON 样本扔进 JSON 转 Scala,拿到 case class 再补解码器,这一步从半小时压到一分钟。
字段类型是怎么推断出来的
JSON 只有一种数字类型,但 Scala 不是。工具按值来分:落在有符号 32 位范围内的整数推断成 Int,超出范围的整数(雪花 ID、毫秒时间戳这种)推断成 Long,带小数点的推断成 Double。如果同一个字段在一个样本里是整数、另一个样本里带小数,它会放宽成 Double,这样遇到带小数那次解码也不会挂。字符串是 String,布尔是 Boolean,标量数组比如 ["en","zh"] 推成 List[String],混合数组 [1,"two",true] 没有单一元素类型能兜住,就放宽成 List[Any]。
嵌套对象不内联,抽成子 case class
Scala 的 case class 不能匿名嵌套,所以每个嵌套对象都会被抽成独立子类,命名规则是父类名加键名。根名设成 User 时,address 嵌套对象会生成一个 case class UserAddress(...),父类上再挂一个 address: UserAddress 字段。子类排在用到它的父类前面,文件从叶子往上读,引用解析很顺,在 REPL 里也不会撞前向引用。
null 包成 Option,这是 Scala 建模可空的地道写法
打开 Option 开关时,值为 null 的字段、或者在对象数组里部分元素有部分元素没有的键,都会被包成 Option[T]。这正是 circe 和 play-json 这类库建模缺失值的方式:派生出的解码器遇到缺失键不抛异常,你省掉了因为漏标一个可选字段而在运行时报错的那一轮折腾。要注意单个对象没有「键缺失」信号,所有字段都算必需;想从一个样本探可选性,把它包成 [{...}] 再粘。
对象数组折叠成一个类型
[{a:1},{a:1,b:2}] 不会生成两个几乎重复的类型,而是折叠成一个 List[T],其中 b 因为不是每个元素都有,会标成 Option。这对接列表型响应特别省事:十个元素里有两个多了一个字段,你拿到的还是一个干净类型,不是十个变体。
一段真实输入输出
输入一段订单响应样本:
{
"orderId": 9999999999,
"amount": 19.9,
"paid": true,
"coupon": null,
"items": [
{ "sku": "A1", "qty": 2 }
]
}
根名设成 Order,Option 开着,输出:
case class OrderItem(sku: String, qty: Int)
case class Order(
orderId: Long,
amount: Double,
paid: Boolean,
coupon: Option[Any],
items: List[OrderItem]
)
注意 orderId 是 9999999999,超过 32 位,所以推成 Long;amount 带小数推成 Double;coupon 是 null 没有类型信息,只能给 Option[Any](知道真实形状的话,粘个非 null 样本就能推出像样子类);items 抽成了 OrderItem。这段代码旁边加一行 deriveDecoder[Order],circe 解码器就齐了。
我自己怎么用它
我维护一个吃十几个上游接口的 Spark 作业,每次对方改字段我都得同步 DTO。以前是打开接口文档逐字段对,现在是直接 hit 一次接口拿真实 payload 粘进来,因为推断的是实际下发的数据,反而比照着可能过期的文档手写更准。Akka HTTP 那边也一样,新 endpoint 的请求体和响应体两个 case class,在写路由之前就提交进 DTO 文件了。配置文件同理,config.json 里的 server 块和 tls 块会变成 Config、ConfigServer、ConfigServerTls,启动时解码一次,某个键拼错会带着清晰路径在解码时报错,而不是稍后在某个请求深处才挂。
要提一句:工具用的是严格的 JSON.parse,JSON5、带注释的 JSON、末尾逗号都会报行列错。先剥掉,或者先过一道格式化。如果你的栈是 TypeScript 前端配 Scala 后端,前端那侧的类型可以用 JSON 转 TypeScript 接口一起生成,两端口径对齐。
全程在浏览器本地跑,粘进来的 JSON 不碰服务器,生产响应这种敏感数据也能放心粘。
Made by Toolora · Updated 2026-06-13