最后更新:2026-05-28
适用场景:JSON 转 Java 对象、接口参数 DTO 设计、Spring Boot 接收 JSON、Jackson 序列化与反序列化、LocalDateTime 时间格式处理、JSON 字段类型不匹配排查
Java 后端开发中,JSON 几乎每天都会遇到。
常见场景包括:
前端传 JSON 参数给后端
后端返回 JSON 给前端
第三方接口返回 JSON
JSON 字符串转 Java 对象
Java 对象转 JSON 字符串
JSON 数组转 List
嵌套 JSON 转嵌套 DTO
LocalDateTime 时间字段格式化
很多接口问题表面上看是 Spring Boot 报错,实际上是 JSON 结构和 Java 实体类不匹配。
比如:
JSON parse error
Cannot deserialize value of type
Required request body is missing
Cannot construct instance of xxx
Java 8 date/time type java.time.LocalDateTime not supported
本文从实际开发角度整理 JSON 转 Java 实体类的完整写法,包括普通对象、数组、嵌套对象、List、Map、LocalDateTime、字段名不一致、常见报错排查等内容。
一、先说结论:JSON转Java实体类的核心规则
JSON 转 Java 实体类,本质上就是把 JSON 中的字段映射到 Java 类的属性上。
最常见的对应关系如下:
| JSON 类型 | 示例 | Java 推荐类型 |
|---|---|---|
| 字符串 | "张三" | String |
| 整数 | 18 | Integer / Long |
| 小数 | 99.99 | BigDecimal |
| 布尔值 | true / false | Boolean |
| 对象 | { "city": "杭州" } | 自定义 DTO |
| 数组 | [1, 2, 3] | List<Integer> |
| 字符串数组 | ["Java", "Spring"] | List<String> |
| 对象数组 | [{ "id": 1 }] | List<自定义DTO> |
| null | null | 包装类型,不建议用基本类型 |
| 时间字符串 | "2026-05-28 10:30:00" | LocalDateTime + 格式化配置 |
几个建议先记住:
1. 接口入参建议用 DTO,不要直接用数据库实体类 Entity
2. 金额字段建议用 BigDecimal,不建议用 double
3. 可为空字段建议用包装类型 Integer / Long / Boolean,不建议用 int / long / boolean
4. 时间字段建议用 LocalDateTime / LocalDate / LocalTime
5. 嵌套 JSON 就用嵌套 DTO
6. JSON 数组就用 List<T>
7. 字段名不一致时用 @JsonProperty
8. 时间格式不一致时用 @JsonFormat 或统一 ObjectMapper 配置
二、准备一个示例JSON
下面用一个用户下单场景举例:
{
"id": 1001,
"username": "zhangsan",
"nickname": "张三",
"age": 28,
"enabled": true,
"balance": 99.50,
"tags": ["Java", "Spring Boot", "JSON"],
"address": {
"province": "浙江省",
"city": "杭州市",
"detail": "西湖区某某路"
},
"orders": [
{
"orderNo": "A202605280001",
"amount": 199.90,
"status": "PAID"
},
{
"orderNo": "A202605280002",
"amount": 59.00,
"status": "CREATED"
}
],
"createdAt": "2026-05-28 10:30:00"
}
这个 JSON 里包含了常见结构:
普通字段:id、username、age
布尔字段:enabled
金额字段:balance
字符串数组:tags
嵌套对象:address
对象数组:orders
时间字段:createdAt
下面把它转换成 Java 实体类。
三、JSON对象转Java实体类
1. 用户DTO
import com.fasterxml.jackson.annotation.JsonFormat;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
public class UserDTO {
private Long id;
private String username;
private String nickname;
private Integer age;
private Boolean enabled;
private BigDecimal balance;
private List<String> tags;
private AddressDTO address;
private List<OrderDTO> orders;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private LocalDateTime createdAt;
public Long getId() {
return id;
}
public UserDTO setId(Long id) {
this.id = id;
return this;
}
public String getUsername() {
return username;
}
public UserDTO setUsername(String username) {
this.username = username;
return this;
}
public String getNickname() {
return nickname;
}
public UserDTO setNickname(String nickname) {
this.nickname = nickname;
return this;
}
public Integer getAge() {
return age;
}
public UserDTO setAge(Integer age) {
this.age = age;
return this;
}
public Boolean getEnabled() {
return enabled;
}
public UserDTO setEnabled(Boolean enabled) {
this.enabled = enabled;
return this;
}
public BigDecimal getBalance() {
return balance;
}
public UserDTO setBalance(BigDecimal balance) {
this.balance = balance;
return this;
}
public List<String> getTags() {
return tags;
}
public UserDTO setTags(List<String> tags) {
this.tags = tags;
return this;
}
public AddressDTO getAddress() {
return address;
}
public UserDTO setAddress(AddressDTO address) {
this.address = address;
return this;
}
public List<OrderDTO> getOrders() {
return orders;
}
public UserDTO setOrders(List<OrderDTO> orders) {
this.orders = orders;
return this;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public UserDTO setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
return this;
}
}
为了减少篇幅,实际项目中可以使用 Lombok:
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.experimental.Accessors;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
@Data
@Accessors(chain = true)
public class UserDTO {
private Long id;
private String username;
private String nickname;
private Integer age;
private Boolean enabled;
private BigDecimal balance;
private List<String> tags;
private AddressDTO address;
private List<OrderDTO> orders;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private LocalDateTime createdAt;
}
2. 地址DTO
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class AddressDTO {
private String province;
private String city;
private String detail;
}
对应 JSON:
{
"province": "浙江省",
"city": "杭州市",
"detail": "西湖区某某路"
}
3. 订单DTO
import lombok.Data;
import lombok.experimental.Accessors;
import java.math.BigDecimal;
@Data
@Accessors(chain = true)
public class OrderDTO {
private String orderNo;
private BigDecimal amount;
private String status;
}
对应 JSON 数组:
[
{
"orderNo": "A202605280001",
"amount": 199.90,
"status": "PAID"
},
{
"orderNo": "A202605280002",
"amount": 59.00,
"status": "CREATED"
}
]
四、使用Jackson把JSON字符串转成Java对象
Java 中处理 JSON,最常用的是 Jackson。
Spring Boot Web 项目通常已经包含 Jackson 相关能力;普通 Java 项目需要自己添加依赖。
1. Maven依赖
普通 Java 项目可以添加:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
如果需要处理 LocalDateTime、LocalDate、LocalTime,建议增加:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
Spring Boot 项目建议跟随 Spring Boot 的依赖管理,不要随便手动指定 Jackson 版本,避免版本冲突。
2. JSON字符串转Java对象
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
public class JsonToObjectDemo {
public static void main(String[] args) throws Exception {
String json = """
{
"id": 1001,
"username": "zhangsan",
"nickname": "张三",
"age": 28,
"enabled": true,
"balance": 99.50,
"tags": ["Java", "Spring Boot", "JSON"],
"address": {
"province": "浙江省",
"city": "杭州市",
"detail": "西湖区某某路"
},
"orders": [
{
"orderNo": "A202605280001",
"amount": 199.90,
"status": "PAID"
}
],
"createdAt": "2026-05-28 10:30:00"
}
""";
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
UserDTO user = objectMapper.readValue(json, UserDTO.class);
System.out.println(user.getUsername());
System.out.println(user.getAddress().getCity());
System.out.println(user.getOrders().get(0).getOrderNo());
}
}
核心代码是:
UserDTO user = objectMapper.readValue(json, UserDTO.class);
这行代码会把 JSON 字符串转换成 UserDTO 对象。
五、Java对象转JSON字符串
反过来也很常见:把 Java 对象转成 JSON 字符串。
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
public class ObjectToJsonDemo {
public static void main(String[] args) throws Exception {
UserDTO user = new UserDTO()
.setId(1001L)
.setUsername("zhangsan")
.setNickname("张三")
.setAge(28)
.setEnabled(true)
.setBalance(new BigDecimal("99.50"))
.setTags(List.of("Java", "Spring Boot", "JSON"))
.setAddress(new AddressDTO()
.setProvince("浙江省")
.setCity("杭州市")
.setDetail("西湖区某某路"))
.setOrders(List.of(
new OrderDTO()
.setOrderNo("A202605280001")
.setAmount(new BigDecimal("199.90"))
.setStatus("PAID")
))
.setCreatedAt(LocalDateTime.of(2026, 5, 28, 10, 30, 0));
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
String json = objectMapper.writeValueAsString(user);
System.out.println(json);
}
}
核心代码是:
String json = objectMapper.writeValueAsString(user);
六、JSON数组转List
如果 JSON 最外层是数组,例如:
[
{
"id": 1001,
"username": "zhangsan"
},
{
"id": 1002,
"username": "lisi"
}
]
不能直接写:
List<UserDTO> users = objectMapper.readValue(json, List.class);
这样会丢失泛型信息,得到的通常不是你想要的 List<UserDTO>。
推荐使用 TypeReference。Jackson 的 TypeReference 用来保留泛型类型信息,适合 List<UserDTO>、Map<String, Object> 这类泛型结构。([Java Doc][2])
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;
public class JsonArrayToListDemo {
public static void main(String[] args) throws Exception {
String json = """
[
{
"id": 1001,
"username": "zhangsan"
},
{
"id": 1002,
"username": "lisi"
}
]
""";
ObjectMapper objectMapper = new ObjectMapper();
List<UserDTO> users = objectMapper.readValue(
json,
new TypeReference<List<UserDTO>>() {}
);
System.out.println(users.size());
System.out.println(users.get(0).getUsername());
}
}
七、JSON对象转Map
有时候 JSON 字段不固定,不适合提前定义 DTO,可以先转成 Map。
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Map;
public class JsonToMapDemo {
public static void main(String[] args) throws Exception {
String json = """
{
"id": 1001,
"username": "zhangsan",
"extra": {
"source": "web",
"level": "vip"
}
}
""";
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> map = objectMapper.readValue(
json,
new TypeReference<Map<String, Object>>() {}
);
System.out.println(map.get("username"));
}
}
但要注意:业务接口入参不建议长期用 Map<String, Object> 代替 DTO。
原因是:
1. 字段不清晰
2. 类型不明确
3. 参数校验不方便
4. 接口文档不直观
5. 后期维护成本高
推荐做法:
临时解析、不固定字段:Map
正式接口入参:DTO
复杂动态结构:JsonNode
八、复杂JSON可以用JsonNode先看结构
如果 JSON 很复杂,或者字段不确定,可以用 JsonNode 先解析。
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonNodeDemo {
public static void main(String[] args) throws Exception {
String json = """
{
"id": 1001,
"username": "zhangsan",
"address": {
"city": "杭州市"
},
"tags": ["Java", "Spring Boot"]
}
""";
ObjectMapper objectMapper = new ObjectMapper();
JsonNode root = objectMapper.readTree(json);
Long id = root.get("id").asLong();
String username = root.get("username").asText();
String city = root.get("address").get("city").asText();
String firstTag = root.get("tags").get(0).asText();
System.out.println(id);
System.out.println(username);
System.out.println(city);
System.out.println(firstTag);
}
}
JsonNode 适合:
第三方接口返回结构不稳定
只想读取其中几个字段
需要判断字段是否存在
不想马上定义完整DTO
但正式业务代码中,能用 DTO 的地方还是优先用 DTO。
九、字段名不一致怎么办?
前端传来的字段可能是下划线风格:
{
"user_name": "zhangsan",
"created_at": "2026-05-28 10:30:00"
}
Java 里一般是驼峰风格:
private String userName;
private LocalDateTime createdAt;
这时可以使用 @JsonProperty。
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class UserRequest {
@JsonProperty("user_name")
private String userName;
@JsonProperty("created_at")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private LocalDateTime createdAt;
}
常用注解包括:
| 注解 | 作用 |
|---|---|
@JsonProperty | 指定 JSON 字段名和 Java 属性名的映射 |
@JsonIgnore | 忽略某个字段 |
@JsonIgnoreProperties | 忽略一组字段或未知字段 |
@JsonFormat | 指定日期、时间等字段格式 |
Jackson 官方注解文档中也说明,@JsonFormat 可以用于指定日期时间序列化和反序列化格式,@JsonIgnore 可用于忽略指定属性。([GitHub][3])
十、未知字段太多怎么办?
前端或第三方接口多传了字段,例如:
{
"id": 1001,
"username": "zhangsan",
"unknownField": "xxx"
}
但 Java 类里没有 unknownField。
可以在 DTO 上加:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class UserDTO {
private Long id;
private String username;
}
含义是:
JSON 中有但 Java 类里没有的字段,忽略掉
这在对接第三方接口时比较常用。
但内部业务接口不建议滥用,字段不一致时最好尽早发现问题。
十一、LocalDateTime怎么处理?
时间字段是 JSON 转 Java 实体类时最容易出问题的地方之一。
常见 JSON 时间格式:
{
"createdAt": "2026-05-28 10:30:00"
}
Java 类型:
private LocalDateTime createdAt;
1. 单个字段使用@JsonFormat
最简单的方式:
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class UserDTO {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private LocalDateTime createdAt;
}
适合:
只有少量时间字段
不同字段格式不一样
想快速解决反序列化失败
2. 全局统一LocalDateTime格式
如果项目中大量接口都使用:
yyyy-MM-dd HH:mm:ss
可以做全局配置。
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Configuration
public class JacksonConfig {
private static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
@Bean
public Jackson2ObjectMapperBuilderCustomizer localDateTimeCustomizer() {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN);
return builder -> {
builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(formatter));
builder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(formatter));
};
}
}
这样项目里的 LocalDateTime 默认按统一格式处理。
3. 普通Java程序注册JavaTimeModule
非 Spring Boot 项目中,可以手动配置 ObjectMapper:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class ObjectMapperFactory {
public static ObjectMapper create() {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter));
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter));
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(javaTimeModule);
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
return objectMapper;
}
}
十二、Spring Boot接口中接收JSON实体类
Spring Boot 中最常见的写法是:
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping
public String createUser(@RequestBody UserDTO userDTO) {
return "接收到用户:" + userDTO.getUsername();
}
}
请求 JSON:
{
"id": 1001,
"username": "zhangsan",
"nickname": "张三",
"age": 28,
"enabled": true
}
请求头要带:
Content-Type: application/json
Spring MVC 的 JSON 消息转换器可以通过 Jackson 读写 JSON,并默认支持 application/json。([Home][4])
如果没有 @RequestBody,Spring Boot 不会按 JSON 请求体去绑定对象,容易出现参数接收不到的问题。
十三、DTO、VO、Entity不要混用
实际项目中经常看到这种写法:
@PostMapping("/users")
public String createUser(@RequestBody UserEntity userEntity) {
// ...
}
不推荐。
更合理的分层是:
DTO / Request:接收前端参数
Entity / DO:数据库表结构
VO / Response:返回给前端的数据结构
示例:
public class UserCreateRequest {
private String username;
private String nickname;
private Integer age;
}
public class UserEntity {
private Long id;
private String username;
private String nickname;
private Integer age;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
public class UserVO {
private Long id;
private String username;
private String nickname;
}
这样做的好处:
1. 接口参数更清晰
2. 数据库字段不直接暴露给前端
3. 返回结果可以自由裁剪
4. 参数校验更方便
5. 后期接口变更更安全
十四、JSON转Java实体类常见错误
1. JSON对象和数组搞反
错误 JSON:
[
{
"id": 1001,
"username": "zhangsan"
}
]
Java 却这样接收:
@RequestBody UserDTO userDTO
这是不匹配的。
数组应该用:
@RequestBody List<UserDTO> users
对象才用:
@RequestBody UserDTO userDTO
判断方法很简单:
最外层是 { } -> Java对象
最外层是 [ ] -> List<T>
2. 字段类型不匹配
JSON:
{
"age": "十八"
}
Java:
private Integer age;
这样会报错,因为 "十八" 不能转换成数字。
正确:
{
"age": 18
}
或者 Java 字段改成:
private String age;
但年龄这类字段一般还是建议用数字。
3. 金额字段使用double
不推荐:
private Double amount;
推荐:
private BigDecimal amount;
金额、价格、余额这类字段建议用 BigDecimal,避免浮点精度问题。
4. null传给基本类型
JSON:
{
"age": null
}
Java:
private int age;
不推荐。
应该使用:
private Integer age;
同理:
int -> Integer
long -> Long
boolean -> Boolean
double -> BigDecimal 或 Double
接口 DTO 中建议优先使用包装类型。
5. LocalDateTime格式不匹配
JSON:
{
"createdAt": "2026/05/28 10:30:00"
}
Java:
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createdAt;
这会失败,因为 JSON 里是:
yyyy/MM/dd HH:mm:ss
而 Java 注解要求:
yyyy-MM-dd HH:mm:ss
解决办法:
1. 统一前端传参格式
2. 修改 @JsonFormat pattern
3. 使用全局 Jackson 配置
6. 没有无参构造方法
Jackson 反序列化普通 Java Bean 时,通常需要能创建对象。
使用 Lombok 时建议加:
@NoArgsConstructor
@AllArgsConstructor
@Data
public class UserDTO {
private Long id;
private String username;
}
普通 Java 类要保留无参构造:
public UserDTO() {
}
使用 Java record 是另一种方式,但老项目和部分框架环境下,普通 DTO 更通用。
7. 字段名不一致
JSON:
{
"user_name": "zhangsan"
}
Java:
private String userName;
如果不做配置,可能接收不到。
解决:
@JsonProperty("user_name")
private String userName;
8. Content-Type不对
请求体明明是 JSON:
{
"username": "zhangsan"
}
但请求头是:
Content-Type: application/x-www-form-urlencoded
后端接口是:
@PostMapping("/users")
public String createUser(@RequestBody UserDTO userDTO) {
return "ok";
}
这种情况下可能出现 415、400 或参数解析失败。
正确请求头:
Content-Type: application/json
十五、JSON转Java实体类排查清单
遇到 JSON 转 Java 对象失败,可以按下面顺序排查:
1. JSON 格式是否正确
2. 最外层是对象还是数组
3. Java 接收类型是否对应
4. 字段名是否一致
5. 字段类型是否一致
6. 时间格式是否一致
7. 是否缺少无参构造方法
8. 是否需要 @JsonProperty
9. 是否需要 @JsonFormat
10. 是否需要 @JsonIgnoreProperties(ignoreUnknown = true)
11. 是否注册 JavaTimeModule
12. Spring Boot 请求头是否是 Content-Type: application/json
13. Controller 参数是否加了 @RequestBody
十六、可以用Json哥辅助生成实体类
手写实体类时,最容易出错的是:
字段名拼错
数组和对象搞反
嵌套结构漏写
数字类型选错
时间字段格式不统一
可以先把 JSON 放到在线工具中格式化、校验,再根据结构生成 Java 实体类。
工具入口:
Json哥 - JSON 在线解析、格式化、校验与实体类转换工具
建议流程:
1. 复制接口 JSON
2. 使用 Json哥 格式化和校验
3. 确认最外层是对象还是数组
4. 根据结构生成 Java 实体类
5. 手动检查字段类型
6. 金额改 BigDecimal
7. 时间字段改 LocalDateTime 并加格式化配置
8. 最后放到项目中测试
工具生成实体类可以提高效率,但不要完全不检查。尤其是金额、时间、ID、枚举、嵌套对象这些字段,最好人工确认一遍。
十七、推荐实体类写法模板
普通接口请求 DTO 推荐这样写:
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class DemoRequest {
private Long id;
private String name;
private Integer age;
private Boolean enabled;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private LocalDateTime createdAt;
}
如果字段名和 JSON 不一致:
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonProperty("user_name")
private String userName;
如果某个字段不参与 JSON:
import com.fasterxml.jackson.annotation.JsonIgnore;
@JsonIgnore
private String internalToken;
十八、实际开发中的建议
1. 入参DTO不要太复杂
一个接口对应一个请求 DTO,字段尽量清晰。
不建议:
@RequestBody Map<String, Object> params
长期使用 Map 会让接口越来越难维护。
2. 前后端约定字段格式
建议团队统一:
字段命名:camelCase
时间格式:yyyy-MM-dd HH:mm:ss
金额类型:字符串或数字,但后端用 BigDecimal
数组:统一用 [],不要对象和数组混用
空值:明确 null、空字符串、空数组的含义
3. 不要把数据库Entity直接暴露给前端
数据库字段经常包含:
deleted
version
createdBy
updatedBy
password
salt
internalStatus
这些不一定适合暴露给前端。
推荐:
Request DTO 接收参数
Entity 保存数据库
Response VO 返回结果
4. 时间字段尽量统一
不要一个接口是:
2026-05-28 10:30:00
另一个接口是:
2026/05/28 10:30:00
再另一个接口是:
2026-05-28T10:30:00
格式越混乱,JSON 反序列化问题越多。
5. 报错信息要完整保留
遇到 JSON 解析失败,不要只截取一句:
JSON parse error
要看完整报错中的字段路径,例如:
through reference chain: UserDTO["orders"]->ArrayList[0]->OrderDTO["amount"]
这类路径能直接定位是哪个字段转换失败。
十九、常见问题FAQ
1. JSON最外层是数组,Java应该怎么接收?
用 List<T>。
Spring Boot Controller 示例:
@PostMapping("/batch")
public String batchCreate(@RequestBody List<UserDTO> users) {
return "接收到数量:" + users.size();
}
Jackson 手动解析:
List<UserDTO> users = objectMapper.readValue(
json,
new TypeReference<List<UserDTO>>() {}
);
2. JSON字段比Java实体类多会报错吗?
看配置。
可以加:
@JsonIgnoreProperties(ignoreUnknown = true)
这样多余字段会被忽略。
但内部接口不建议长期忽略未知字段,因为字段变更时可能不容易发现问题。
3. JSON字段比Java实体类少会怎么样?
对应 Java 字段一般会是 null。
例如 JSON:
{
"username": "zhangsan"
}
Java:
private String username;
private Integer age;
最终:
username = "zhangsan"
age = null
所以 DTO 中可为空字段建议用包装类型。
4. LocalDateTime为什么经常转换失败?
常见原因:
1. JSON时间格式和Java配置格式不一致
2. 没有注册 JavaTimeModule
3. 前端传的是时间戳,后端用 LocalDateTime
4. 前端传的是 yyyy/MM/dd,后端要求 yyyy-MM-dd
5. 字段上没有 @JsonFormat,也没有全局配置
解决优先级:
1. 统一前后端时间格式
2. 字段上加 @JsonFormat
3. Spring Boot 中做全局 Jackson 配置
4. 普通 Java 程序中注册 JavaTimeModule
5. 金额字段用BigDecimal还是Double?
推荐 BigDecimal。
private BigDecimal amount;
不推荐:
private Double amount;
金额计算对精度敏感,用 Double 容易引入浮点精度问题。
6. JSON转实体类一定要用Jackson吗?
不一定。
Java 中常见 JSON 库包括:
Jackson
Gson
Fastjson / Fastjson2
Spring Boot 默认生态中 Jackson 使用非常广。Spring MVC 的 JSON 转换器也可以通过 Jackson 读写 JSON。([Home][4])
如果是 Spring Boot 项目,优先使用默认 Jackson,除非团队有明确选型要求。
7. DTO字段可以用基本类型吗?
可以,但不建议作为接口入参默认选择。
例如:
private int age;
如果前端不传 age,或者传 null,就容易出现问题。
更推荐:
private Integer age;
布尔值也建议:
private Boolean enabled;
而不是:
private boolean enabled;
二十、最终总结
JSON 转 Java 实体类并不复杂,核心就是三件事:
1. JSON结构和Java类型要对应
2. 字段名和字段类型要匹配
3. 时间、金额、数组、嵌套对象要单独注意
推荐规则:
JSON对象 -> Java DTO
JSON数组 -> List<T>
嵌套对象 -> 嵌套 DTO
金额 -> BigDecimal
时间 -> LocalDateTime + @JsonFormat 或全局配置
字段名不一致 -> @JsonProperty
未知字段 -> @JsonIgnoreProperties(ignoreUnknown = true)
动态结构 -> JsonNode 或 Map
正式接口 -> DTO,不要长期用 Map
实际开发中,建议先用 JSON 工具格式化和校验,再生成 Java 实体类,最后结合业务手动调整字段类型。
可以配合使用:
Json哥 - JSON 在线解析、格式化、校验与实体类转换工具
这样能减少很多低级错误,尤其是嵌套对象、数组、LocalDateTime、BigDecimal 这些容易出问题的字段。
二十一、相关文章
Java 环境还没配好,可以先看:
Java开发环境配置专题:JDK、IDEA、Maven、Gradle、版本兼容与常见报错
常用 Java 工具可以看:
Spring Boot 开发注意事项可以看:
后续建议继续阅读:
Spring Boot @RequestBody接收JSON参数完整教程
Jackson ObjectMapper使用教程:Java对象、List、Map与JSON字符串互转
Jackson LocalDateTime序列化与反序列化完整解决方案
Spring Boot 415 Unsupported Media Type解决办法
Spring Boot JSON parse error常见原因与解决办法
Fastjson、Jackson、Gson有什么区别?Java项目JSON库选型指南
更新记录
2026-05-28:
- 创建 JSON 转 Java 实体类完整教程
- 增加普通对象、数组、嵌套对象、List、Map、JsonNode 示例
- 增加 Jackson ObjectMapper 使用示例
- 增加 LocalDateTime 字段格式化处理
- 增加 Spring Boot @RequestBody 接收 JSON 示例
- 增加字段名不一致、未知字段、金额类型、常见报错排查
- 增加 Json哥 JSON 工具入口
JSON转Java实体类完整教程:对象、数组、嵌套结构与LocalDateTime处理
https://java.li/archives/json-to-java-entity-class
评论