跳到主要内容

JSON 转 Java 类实战:从字段映射到 Jackson 注解

把一段 JSON 粘进来就能拿到带 getter setter 的 Java 实体类,本文讲清字段类型推断、嵌套对象拆分、Jackson 与 Gson 注解的取舍,省去手写 POJO 的重复劳动。

发布于 作者 李雷
#JSON #Java #POJO #Jackson #后端开发

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,它就必须装箱成 IntegerLongDoubleBoolean。否则一个缺失值会被悄悄读成 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_idis_paiduser_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"}] 不会生成两个几乎重复的类,而是一个同时带 idname 的类,并且因为 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