点击上方“程序员蜗牛g”,选择“设为星标”
跟蜗牛哥一起,每天进步一点点
程序员蜗牛g
大厂程序员一枚 跟蜗牛一起 每天进步一点点
33篇原创内容
**
公众号
对于 Java 开发者来说,JSON 字段的不确定性就像一个无形的陷阱—— 前端改个字段名、加个动态属性,后端解析立刻崩溃。
设想这样一个场景:
{ "name": "icoderoad", "mobile": "13900000000", "extFields": { "email": "icoderoad@gmail.com", "age": 2 }}
或者换一种写法:
{ "name": "icoderoad", "mobile": "13900000000", "email": "icoderoad@gmail.com", "age": 22}
而你的后端实体类 /src/main/java/com/icoderoad/model/User.java 却是这样的:
@Data@AllArgsConstructor@NoArgsConstructorpublic class User { private String name; private String mobile;}
这种不匹配让无数后端开发者陷入了“字段地狱”。 本文将深入解析三种经典解决方案,让 Spring Boot 从容应对任意结构的 JSON 数据!
方案一:Map接收法 —— 轻量又实用
当仅需临时存储额外字段时,用 Map<String, Object> 是最直接的方式。
示例代码
路径:/src/main/java/com/icoderoad/model/User.java
package com.icoderoad.model;
import lombok.*;import java.util.Map;
@Data@AllArgsConstructor@NoArgsConstructorpublic class User { private String name; private String mobile; protected Map<String, Object> extFields;}
路径:/src/main/java/com/icoderoad/controller/UserController.java
package com.icoderoad.controller;
import com.icoderoad.model.User;import com.icoderoad.util.UserUtil;import org.springframework.web.bind.annotation.*;
@RestController@RequestMapping("/user")public class UserController {
@PostMapping("/json-map") public User getUser(@RequestBody User user) { UserUtil.print(user, "email", "age"); return user; }}
路径:/src/main/java/com/icoderoad/util/UserUtil.java
package com.icoderoad.util;
import com.icoderoad.model.User;import org.apache.commons.lang3.ArrayUtils;
public final class UserUtil { private UserUtil() {}
public static void print(User user, String... keys) { System.out.println("name: " + user.getName()); System.out.println("mobile: " + user.getMobile()); if (ArrayUtils.isNotEmpty(keys)) { for (String k : keys) { System.out.println(k + ": " + user.getExtFields().get(k)); } } }}
控制台输出:
name: icoderoadmobile: 13900000000email: icoderoad@gmail.comage: 22
优点:实现简单,适合轻量场景。缺点:可读性不强,维护复杂结构时代码臃肿。
方案二:JsonNode接收法 —— 结构复杂的利器
当 JSON 层次较深或结构不固定时,JsonNode 是更专业的选择。 它来自 Jackson 库,可以精确访问任意节点。
Maven 依赖
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.13.0</version></dependency>
实体类
路径:/src/main/java/com/icoderoad/model/User.java
package com.icoderoad.model;
import com.fasterxml.jackson.databind.JsonNode;import lombok.*;
@Data@AllArgsConstructor@NoArgsConstructorpublic class User { private String name; private String mobile; private JsonNode extFields;}
控制器
路径:/src/main/java/com/icoderoad/controller/UserController.java
@PostMapping("/json-node")public User getUserByJsonNode(@RequestBody User user) { UserUtil.print(user, "email", "age"); return user;}
测试输出
name: icoderoadmobile: 13900000000email: icoderoad@gmail.comage: 22
优势:
- 可解析任意深度的嵌套结构;
- 支持动态访问节点属性;
- 与 Jackson 紧密集成。
劣势:
- 操作较复杂;
- 学习曲线略高。
方案三:@JsonAnySetter / @JsonAnyGetter —— Jackson 双剑合璧
这对注解是 JSON 适配的“终极解决方案”。
它能在反序列化和序列化过程中,动态接收与输出未知字段。
示例代码
路径:/src/main/java/com/icoderoad/model/User.java
package com.icoderoad.model;
import com.fasterxml.jackson.annotation.*;import lombok.*;import java.util.*;
@Data@AllArgsConstructor@NoArgsConstructorpublic class User { private String name; private String mobile;
private Map<String, Object> extFields = new HashMap<>();
@JsonAnySetter public void setUnknownField(String key, Object value) { extFields.put(key, value); }
@JsonAnyGetter public Map<String, Object> getUnknownFields() { return extFields; }}
控制器
路径:/src/main/java/com/icoderoad/controller/UserController.java
@PostMapping("/json-annotation")public User getUserByAnnotation(@RequestBody User user) { UserUtil.print(user, "email", "age"); return user;}
输出结果:
name: icoderoadmobile: 13900000000email: icoderoad@gmail.comage: 22
优势:
- 动态接收和输出未知字段;
- 代码更清晰、语义更强;
- 特别适合字段频繁变化的系统。
案例实操:在线商城中的动态促销信息
假设我们的商品 JSON 如下:
{ "productName": "智能手表", "price": 1999.00, "stock": 100, "promotionInfo": { "discount": 0.8, "fullReduce": { "condition": 2000, "reduction": 500 } }}
后端实体类 /src/main/java/com/icoderoad/model/Product.java:
@Data@AllArgsConstructor@NoArgsConstructorpublic class Product { private String productName; private Double price; private Integer stock; private Map<String, Object> promotionInfo;}
控制器 /src/main/java/com/icoderoad/controller/ProductController.java:
@RestController@RequestMapping("/product")public class ProductController {
@PostMapping("/map") public Product saveProduct(@RequestBody Product product) { System.out.println("商品名称:" + product.getProductName()); System.out.println("价格:" + product.getPrice()); System.out.println("库存:" + product.getStock()); System.out.println("促销信息:" + product.getPromotionInfo()); return product; }}
若促销信息结构更复杂,可改用 JsonNode:
private JsonNode promotionInfo;
即可轻松访问嵌套节点:
Double discount = product.getPromotionInfo().get("discount").asDouble();
如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看。
关注公众号:woniuxgg,在公众号中回复:笔记 就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!