Lombok配合JsonDeserializer实现JSON扁平化反序列化及自定义类型转换
方案1:使用Jackson根节点解包特性(最简洁推荐)
Jackson自带UNWRAP_ROOT_VALUE反序列化特性,可以自动提取根节点下指定名称的内层节点作为实际反序列化数据源,完全不需要冗余嵌套类,也不需要手写全量字段的反序列化逻辑,字段级的自定义反序列化注解可以正常生效。
完整实现代码如下:
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonRootName; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import lombok.Builder; import lombok.Value; import java.awt.Dimension; // 若为自定义Dimension类替换对应包名即可 import java.io.IOException; @Builder @Value // 指定要提取的根节点名称为data @JsonRootName("data") public class Box3 { @JsonProperty("name") String name; @JsonProperty("size") // 字段级自定义反序列化器直接生效 @JsonDeserialize(using = SizeDeserializer.class) Dimension size; public static Box3 of(String json) throws IOException { // 局部创建ObjectMapper配置,不影响全局ObjectMapper实例 ObjectMapper mapper = new ObjectMapper() .enable(DeserializationFeature.UNWRAP_ROOT_VALUE); return mapper.readValue(json, Box3.class); } // size字段反序列化器示例 static class SizeDeserializer extends JsonDeserializer<Dimension> { @Override public Dimension deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { String sizeStr = p.getText().trim(); String[] wh = sizeStr.split("x"); int width = Integer.parseInt(wh[0]); int height = Integer.parseInt(wh[1]); return new Dimension(width, height); } } }
方案2:优化自定义反序列化器(兼容原有全局配置场景)
如果因为业务限制不能开启根节点解包特性,可以优化自定义类反序列化器的实现,不需要手动读取赋值每个字段,而是将data节点交给Jackson上下文反序列化,自动触发所有字段级的反序列化逻辑,避免手写字段赋值的冗余代码。
完整实现代码如下:
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.Builder; import lombok.Value; import java.awt.Dimension; import java.io.IOException; @Builder @Value @JsonDeserialize(using = Box3.Box3Deserializer.class) public class Box3 { @JsonProperty("name") String name; @JsonProperty("size") @JsonDeserialize(using = SizeDeserializer.class) Dimension size; public static Box3 of(String json) throws IOException { return new ObjectMapper().readValue(json, Box3.class); } static class Box3Deserializer extends JsonDeserializer<Box3> { @Override public Box3 deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { ObjectNode root = p.getCodec().readTree(p); // 提取data节点后交给Jackson原生反序列化逻辑处理,自动触发字段级注解 JsonParser dataParser = root.get("data").traverse(p.getCodec()); dataParser.nextToken(); // 这里使用ctxt.readValue不会触发循环调用,因为我们跳过了根节点匹配逻辑 return ctxt.readValue(dataParser, Box3.class); } } static class SizeDeserializer extends JsonDeserializer<Dimension> { @Override public Dimension deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { String sizeStr = p.getText().trim(); String[] wh = sizeStr.split("x"); return new Dimension(Integer.parseInt(wh[0]), Integer.parseInt(wh[1])); } } }
内容的提问来源于stack exchange,提问作者AweSIM
相关产品推荐
相关产品推荐

