如何使用Jackson反序列化字段可为字符串或对象的JSON负载
解决Jackson反序列化同一字段两种格式的问题
这个场景我之前也碰到过,要让Jackson能同时处理字符串形式的ID和展开式的对象,自定义反序列化器是最靠谱的方案。下面一步步来实现:
1. 定义实体类
首先我们需要先定义对应的Java实体,先写Category类:
import lombok.Data; @Data public class Category { private String id; private String name; }
然后是包含category字段的Movie类,注意要给category字段加上@JsonDeserialize注解,指定我们后续要写的自定义反序列化器:
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import lombok.Data; @Data public class Movie { private String id; @JsonDeserialize(using = CategoryDeserializer.class) private Category category; private String title; private String description; }
2. 实现自定义反序列化器
接下来写核心的CategoryDeserializer,它继承自Jackson的JsonDeserializer<Category>,主要逻辑是判断当前JSON节点是字符串还是对象,分别处理:
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import java.io.IOException; public class CategoryDeserializer extends JsonDeserializer<Category> { @Override public Category deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { JsonNode node = p.getCodec().readTree(p); Category category = new Category(); if (node.isTextual()) { // 处理字符串形式:"/category/12" -> 提取id为"12" String categoryStr = node.asText(); // 这里假设字符串格式固定为"/category/{id}",如果格式有变化可以调整正则或分割逻辑 String id = categoryStr.replaceAll("^/category/", ""); category.setId(id); // 如果需要name字段,这里可以根据id去查询或者留空,看业务需求 } else if (node.isObject()) { // 处理对象形式:直接反序列化为Category category = p.getCodec().treeToValue((ObjectNode) node, Category.class); } return category; } }
3. 测试验证
最后我们可以写个测试代码,验证两种JSON格式都能正确反序列化:
import com.fasterxml.jackson.databind.ObjectMapper; public class TestDeserialization { public static void main(String[] args) throws IOException { ObjectMapper mapper = new ObjectMapper(); // 测试未展开形式的JSON String movieJson1 = "{\"id\":\"777424881071\",\"category\":\"/category/12\",\"title\":\"ACADEMY DINOSAUR\",\"description\":\"A Epic Drama of ...\"}"; Movie movie1 = mapper.readValue(movieJson1, Movie.class); System.out.println("未展开形式解析结果:" + movie1); // 测试展开形式的JSON String movieJson2 = "{\"id\":\"777424881071\",\"category\":{\"id\":\"12\",\"name\":\"Children\"},\"title\":\"ACADEMY DINOSAUR\",\"description\":\"A Epic Drama of ...\"}"; Movie movie2 = mapper.readValue(movieJson2, Movie.class); System.out.println("展开形式解析结果:" + movie2); } }
运行这个测试类,你会看到两种格式的JSON都能正确解析成Movie对象,category字段的id都能正确获取,展开形式的name也能正常读取。
如果你的字符串格式不是固定的/category/{id},只需要调整反序列化器里的字符串解析逻辑就行,比如用正则匹配提取ID部分,或者根据实际的字符串格式做分割。
内容的提问来源于stack exchange,提问作者Jmini




