JSON 转 Java 类实战:从字段映射到 Jackson 注解
把一段 JSON 粘进来就能拿到带 getter setter 的 Java 实体类,本文讲清字段类型推断、嵌套对象拆分、Jackson 与 Gson 注解的取舍,省去手写 POJO 的重复劳动。
JSON 转 Java 类实战:从字段映射到 Jackson 注解
接第三方接口的时候,最磨人的环节往往不是写业务逻辑,而是照着一份 JSON 文档手敲实体类。一个响应二十多个字段,每个都要写 private 声明、getter、setter,还要盯着 snake_case 的 key 加注解。敲完一遍发现对方文档又更新了,再来一遍。这种纯体力活,完全可以交给工具。
字段怎么从 JSON 映射到 Java 属性
核心是两件事:名字和类型。
JSON 的 key 习惯用 snake_case 或带连字符,而 Java 字段要的是合法标识符和 camelCase。所以 site_admin 会变成 siteAdmin,is-active 变成 isActive,created_at 变成 createdAt。这一步如果手敲,大批量很容易写错一两个字母,反序列化时就静默绑空。
类型推断是另一半。JSON 只有一种数字类型,但 Java 分得很细。判断规则是:能放进 32 位的整数推断成 int,更大的整数(毫秒时间戳、雪花 ID)推断成 long,带小数的推断成 double。同一个字段在一份样本里是整数、在另一份里带小数,就放宽到 double 这个安全超集。
最容易被忽略的是装箱。Java 的基本类型不能装 null,所以只要某个字段在对象数组的部分元素里缺失、或出现过 null,它就必须装箱成 Integer、Long、Double、Boolean。否则一个缺失值会被悄悄读成 0 或 false,你排查半天都找不到根源。
一段真实输入输出
输入这样一段 JSON:
{
"order_id": 90071992547409,
"amount": 38.5,
"is_paid": true,
"customer": { "user_name": "lilei", "vip_level": 3 }
}
选 POJO + Jackson,根名设为 Order,生成出来大致是:
public class Order {
@JsonProperty("order_id")
private long orderId;
private double amount;
@JsonProperty("is_paid")
private boolean isPaid;
private OrderCustomer customer;
// getter / setter 省略
}
public class OrderCustomer {
@JsonProperty("user_name")
private String userName;
@JsonProperty("vip_level")
private int vipLevel;
// getter / setter 省略
}
注意三个细节:order_id 的值超过了 int 上限,自动推断成 long;customer 这个嵌套对象被抽成了独立的 OrderCustomer 类,而不是塞成 Map<String, Object>;只有名字真正发生变化的字段(order_id、is_paid、user_name)才挂了注解,amount 这种本来就对得上的不加,输出更干净。
Jackson 还是 Gson,注解只在必要时出现
用 ObjectMapper 序列化(Spring Boot 默认)就选 Jackson,生成 @JsonProperty;用 com.google.gson.Gson 就选 Gson,生成 @SerializedName。
为什么不是每个字段都加注解?因为注解的唯一作用是把不一致的名字接起来。name 转过去还是 name,不用帮忙就能绑;site_admin 变成了 siteAdmin,必须靠 @JsonProperty("site_admin") 把下划线那一版的 key 找回来。如果你本来就在 mapper 上配了全局命名策略(Jackson 的 SNAKE_CASE、Gson 的 LOWER_CASE_WITH_UNDERSCORES),那就把注解风格设成无,一个都不生成。
嵌套对象和对象数组
嵌套对象会逐层抽成命名类,User.address 变成被 address 字段引用的 UserAddress,于是你拿到的是一张真正的类型图。
对象数组的处理更聪明:它会把数组里所有元素折叠进同一个类。[{"id":1},{"id":1,"name":"x"}] 不会生成两个几乎重复的类,而是一个同时带 id 和 name 的类,并且因为 name 在第一个元素里缺失,它被装箱处理。这正好对应真实接口在不同 payload 间的字段波动,也顺手帮你看清了哪些字段其实是可选的。
我自己的用法
我接外部接口时的习惯是:先用 /zh/t/json-formatter/ 把对方丢过来的一坨压缩 JSON 格式化、确认结构没问题,再整段贴进 /zh/t/json-to-java/ 生成实体。如果对方接口偶尔会少传字段,我会多抓几次响应、拼成数组一起贴,这样工具能帮我把真正可选的字段标出来并装箱,省得上线后才发现某个 null 被读成了 0。整个过程比手敲快太多,而且不会因为眼花漏掉一个注解。
需要给金额这类字段做精确小数时,记得生成后手动把 double 改成 BigDecimal,因为 JSON 本身不带"这个数需要十进制精度"的信号,工具没法替你判断。
如果你的技术栈不是 Java,同一套思路还有对应工具:接 .NET 用 json-to-csharp,写 Go 服务用 json-to-go-struct,这两个的字段推断逻辑是一致的,迁移成本很低。
Made by Toolora · Updated 2026-06-13