[序列化/JSON/Java/Utils] JACKSON 概述
1 概述:Jackson
Jackson 库的介绍
Jackson是由 FasterXML 开发的、用来序列化和反序列化json的Java的开源框架。
- 其支持多种数据格式(如 JSON、XML、YAML 等)。
Spring MVC/Spring Boot框架的默认json解析器便是Jackson。- 其通过流式 API 提供高性能的序列化和反序列化,适合企业级复杂场景
Jackson库的版本沿革
- Jackson 的
1.x版本的包名是org.codehaus.jackson,当升级到2.x版本时,包名变为com.fasterxml.jackson
本文讨论的内容是基于最新的 Jackson 的 2.9.1 版本。
Jackson的组成
- Jackson 的核心模块由三部分组成:
jackson-core,核心包,提供基于"流模式"解析的相关 API,它包括JsonPaser和JsonGenerator。Jackson 内部实现正是通过高性能的流模式 API 的 JsonGenerator 和 JsonParser 来生成和解析 json。
jackson-annotations,注解包,提供标准注解功能;
jackson-databind,数据绑定包提供基于"对象绑定" 解析的相关 API (
ObjectMapper) 和"树模型" 解析的相关 API (JsonNode);
基于"对象绑定" 解析的 API 和"树模型"解析的 API 依赖基于"流模式"解析的 API。
Jackson最常用的 API 就是基于"对象绑定" 的ObjectMapper。
JSON序列化库的对比
- 与其他 Java 的 json 的框架 Gson / Fastjson 等相比:
Gson:Gson 是 Google 开发的轻量级 JSON 库,零外部依赖,适合 Android 开发。它通过 TypeToken 解决泛型擦除问题,支持复杂对象图的转换,但性能相对较慢。
Fatsjson/Fastjson2Fastjson2 是阿里巴巴开发的高性能 JSON 库,作为 Fastjson 1.x 的重构版本,修复了安全漏洞并提升了性能,适合高吞吐量场景。
[JSON] Fastjson 之版本对比:Fastjson vs Fastjson2 - 博客园/千千寰宇
- 序列与反序列化的性能: Jackson 解析大的 json 文件速度比较快;
性能测试结果显示,Fastjson2 的序列化速度领先 20%-30%,而 Jackson 的反序列化性能与其接近,Gson 在两方面都稍逊一筹。
- 资源消耗: Jackson 运行时占用内存比较低,性能比较好;
- 可扩展性: Jackson 有灵活的 API,可以很容易进行扩展和定制。(相比 Fastjson)
- 安全性: Jackson 和 Fastjson2 的安全性较高,尤其是 Fastjson2 修复了 1.x 的历史漏洞(如反序列化 RCE)。
Gson 的安全性也较好,但在处理复杂场景时可能需要额外配置。
Fastjson2 对 Fastjson 1.x 的部分 API 提供兼容支持,迁移成本较低。
- 稳定性:Jackson 和 Gson 的 API 稳定性较高,适合长期维护的项目。
- json 序列化库选型的最佳实践
- 企业级应用:推荐 Jackson,因其安全性高、生态完善且支持多格式。
- 性能优先场景:推荐 Fastjson2,其序列化性能优于其他库。
- 轻量级开发:推荐 Gson,因其简单易用、零配置。
Maven 依赖坐标
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
jackson.version= 2.13.3 / 2.19.2 / ...
- 依赖说明
jackson-databind依赖jackson-core和jackson-annotations故:当添加
jackson-databind之后,jackson-core和jackson-annotations也随之添加到 Java 项目工程中。
在添加相关依赖包之后,就可以使用 Jackson。
2 实践案例
样例DTO
public class Person {
// 正常case
private String name;
// 空对象case
private Integer age;
// 日期转换case
private Date date;
// 默认值case
private int height;
}
样例数据
- 样例数据1
{
"cmsMajorVersionNumber": 1,
"cmsMinorVersionNumber": null,
"cmsPackCnt": 2,
"cmsPackInfo": [
{
"id": "cms123456",
"chargeEndNo": 1,
"cmsCellCnt": 96,
"cmsModuleXCellBlock": [
3.72,
3.71,
3.7,
3.72
],
"cmsCellThremistorCnt": 4,
"cmsModuleXTempSensor": [
28.5,
29.0,
28.8,
29.2
],
"cmsCellVoltMax": 3.72,
"cmsCellVoltMin": 3.7,
"cmsCellVoltAvg": 3.71,
"cmsCellVoltMaxIndex": 2,
"cmsCellVoltMinIndex": 5,
"cmsPackTempMin": 28.5,
"cmsPackTempMax": 29.2,
"cmsPackTempMinIdx": 1,
"cmsPackTempMaxIdx": 3,
"cmsPackTempAvg": 28.9,
"cmsPPack": -3200.5,
"cmsSoh": 95.2,
"cmsChgAmount": 12450.6,
"cmsIsolationResistance": 5000,
"cmsVPackBmu": 355.6,
"cmsBalanceSwCellBlock": [
0,
1,
0,
1
],
"cmsCycleCnt": 215,
"ratedCurrent": 200.0,
"peakCurrent": 350.0,
"ratedVolt": 360.0,
"ratedCapacity": 120.0,
"currentCapacity": 75.3,
"cmsSoc": 62.5
}
]
}
CASE 基本的序列化与反序列化
@Test
public void test1() throws IOException {
ObjectMapper mapper = new ObjectMapper();
// 造数据
Person person = new Person();
person.setName("Tom");
person.setAge(40);
person.setDate(new Date());
//序列化
String jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(person);
System.out.println(jsonString);
//反序列化
Person deserializedPerson = mapper.readValue(jsonString, Person.class);
System.out.println(deserializedPerson);
}
out
序列化
{
"name" : "Tom",
"age" : 40,
"date" : 1594634846647,
"man" : null,
"height" : 0
}
反序列化
JackSonTest.Person(name=Tom, age=40, date=Mon Jul 13 18:07:26 CST 2020, man=null, height=0)
- 解释
ObjectMapper通过writeValue系列方法将 java 对象序列化为 json,并将 json 存储成不同的格式 :String(writeValueAsString),Byte Array(writeValueAsString),Writer,File,OutStream和DataOutput。
ObjectMapper通过readValue系列方法从不同的数据源像 String , Byte Array, Reader,File,URL, InputStream 将 json 反序列化为 java 对象。
CASE ObjectMapper对象 的统一配置
- 在调用 writeValue 或调用 readValue 方法之前,往往需要设置 ObjectMapper 的相关配置信息。这些配置信息应用 java 对象的所有属性上。
//在反序列化时,忽略在 json 中存在但 Java 对象不存在的属性
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);//避免因未知属性: `type`而报错, Method threw 'com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException' exception.
//反序列化时,若匹配不到的枚举值,直接赋 null,而非抛异常
mapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);//匹配不到的枚举值,直接赋 null。避免因枚举值被移除或不存在,而报错: "com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `com.xxx.protobuf.Xxx$XxxState$Xxx$SystemMode` from String "UNKNOWN": not one of the values accepted for Enum class: [SYSTEM_MODE_IDLE, SYSTEM_MODE_BUSY, UNRECOGNIZED]"
////Object realData = mapper.treeToValue(dataNode, clazz);// realData 已是强类型对象
//在序列化时日期格式默认为 yyyy-MM-dd'T'HH:mm:ss.SSSZ
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
//在序列化时自定义时间日期格式
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
//在序列化时忽略值为 null 的属性
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
//在序列化时忽略值为默认值的属性
mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_DEFAULT);
更多配置信息可以查看 Jackson 的
DeserializationFeature,SerializationFeature和Include。
CASE 使用注解
- Jackson 根据它的默认方式序列化和反序列化 java 对象,若根据实际需要,灵活的调整它的默认方式,可以使用
Jackson的注解。常用的注解及用法如下。
| 注解 | 用法 |
|---|---|
| @JsonProperty | 用于属性,把属性的名称序列化时转换为另外一个名称。示例:@JsonProperty("birth_date") private Date birthDate |
| @JsonIgnore | 可用于字段、getter/setter、构造函数参数上,作用相同,都会对相应的字段产生影响。使相应字段不参与序列化和反序列化。 |
| @JsonIgnoreProperties | 该注解是类注解。该注解在Java类和JSON不完全匹配的时候使用。 |
| @JsonFormat | 用于属性或者方法,把属性的格式序列化时转换成指定的格式。示例:@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm") public Date getBirthDate() |
| @JsonPropertyOrder | 用于类, 和 @JsonProperty 的index属性类似,指定属性在序列化时 json 中的顺序 , 示例:@JsonPropertyOrder({ "birth_Date", "name" }) public class Person |
| @JsonCreator | 用于构造方法,和 @JsonProperty 配合使用,适用有参数的构造方法。示例:@JsonCreator public Person(@JsonProperty("name")String name) |
| @JsonAnySetter | 用于属性或者方法,设置未反序列化的属性名和值作为键值存储到 map 中 @JsonAnySetter public void set(String key, Object value) |
| @JsonAnyGetter | 用于方法 ,获取所有未序列化的属性 public Map<String, Object> any() |
| @JsonNaming | 类注解。序列化的时候该注解可将驼峰命名的字段名转换为下划线分隔的小写字母命名方式。反序列化的时候可以将下划线分隔的小写字母转换为驼峰命名的字段名。示例:@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) |
| @JsonRootName | 类注解。需开启mapper.enable(SerializationFeature.WRAP_ROOT_VALUE),用于序列化时输出带有根属性名称的 JSON 串,形式如 {"root_name":{"id":1,"name":"zhangsan"}}。但不支持该 JSON 串反序列化。 |
- 使用示例
// 用于类,指定属性在序列化时 json 中的顺序
@JsonPropertyOrder({"date", "user_name"})
// 批量忽略属性,不进行序列化
@JsonIgnoreProperties(value = {"other"})
// 用于序列化与反序列化时的驼峰命名与小写字母命名转换
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public static class User {
@JsonIgnore
private Map<String, Object> other = new HashMap<>();
// 正常case
@JsonProperty("user_name")
private String userName;
// 空对象case
private Integer age;
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
// 日期转换case
private Date date;
// 默认值case
private int height;
public User() {
}
// 反序列化执行构造方法
@JsonCreator
public User(@JsonProperty("user_name") String userName) {
System.out.println("@JsonCreator 注解使得反序列化自动执行该构造方法 " + userName);
// 反序列化需要手动赋值
this.userName = userName;
}
@JsonAnySetter
public void set(String key, Object value) {
other.put(key, value);
}
@JsonAnyGetter
public Map<String, Object> any() {
return other;
}
// 本文默认省略getter、setter方法
}
单元测试:
@Test
public void test3() throws IOException {
ObjectMapper mapper = new ObjectMapper();
// 造数据
Map<String, Object> map = new HashMap<>();
map.put("user_name", "Tom");
map.put("date", "2020-07-26 19:28:44");
map.put("age", 100);
map.put("demoKey", "demoValue");
String jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(map);
System.out.println(jsonString);
System.out.println("反序列化");
User user = mapper.readValue(jsonString, User.class);
System.out.println(user);
System.out.println("序列化");
jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(user);
System.out.println(jsonString);
}
out
{
"date" : "2020-07-26 19:28:44",
"demoKey" : "demoValue",
"user_name" : "Tom",
"age" : 100
}
反序列化
@JsonCreator 注解使得反序列化自动执行该构造方法Tom
JackSonTest.User(other={demoKey=demoValue}, userName=Tom, age=100, date=Sun Jul 26 19:28:44 CST 2020, height=0)
序列化
{
"date" : "2020-07-26 19:28:44",
"user_name" : "Tom",
"age" : 100,
"height" : 0,
"demoKey" : "demoValue"
}
CASE 将强类型的 dto java 对象转换为 JsonNode
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper objectMapper = new ObjectMapper();
JsonNode rootNode = objectMapper.valueToTree( myDto );
CASE 将 json / dto 对象转换为平铺的嵌套层次的K-V Map
- 需求描述
将 【样例数据1】 的 json / dto 转为平铺的嵌套层次的K-V Map。
其Map的内容,例如:
"cmsMajorVersionNumber" : 1
"cmsMinorVersionNumber": null
"cmsPackCnt": 2
"cmsPackInfo[0].id" : "cms123456"
"cmsPackInfo[0].chargeEndNo": 1
"cmsPackInfo[0].cmsCellCnt": 96
...
- 代码实现
- JacksonUtils#flatten (本文档)
Z 最佳实践
JacksonUtils
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 基于 jackson 的反序列化工具
* @updateTime 2025-09-10 23:51
*/
//@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class JacksonUtils {
private static final Logger log = LoggerFactory.getLogger(JacksonUtils.class);
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
public static String NESTED_LEVEL_SEPARATOR_VALUE = System.getProperty("json.nestedLevelSeparator", ".");
public static final String NESTED_LEVEL_SEPARATOR_PARAM = "json.nestedLevelSeparator";
public static String ARRAY_ELEMENT_LEFT_FLAG_VALUE = "[";
public static final String ARRAY_ELEMENT_LEFT_FLAG_PARAM = "json.arrayElement.leftFlag";
public static String ARRAY_ELEMENT_RIGHT_FLAG_VALUE = "]";
public static final String ARRAY_ELEMENT_RIGHT_FLAG_PARAM = "json.arrayElement.rightFlag";
static {
OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
OBJECT_MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
//flatten 方法的相关配置
log.info("{} : {}", "json.nestedLevelSeparator", NESTED_LEVEL_SEPARATOR_VALUE);
ARRAY_ELEMENT_LEFT_FLAG_VALUE = System.getProperty("json.arrayElement.leftFlag", "[");
log.info("{} : {}", "json.arrayElement.leftFlag", ARRAY_ELEMENT_LEFT_FLAG_VALUE);
ARRAY_ELEMENT_RIGHT_FLAG_VALUE = System.getProperty("json.arrayElement.rightFlag", "]");
log.info("{} : {}", "json.arrayElement.rightFlag", ARRAY_ELEMENT_RIGHT_FLAG_VALUE);
}
@SneakyThrows
public static String writeValueAsString(Object o) {
return OBJECT_MAPPER.writeValueAsString(o);
}
@SneakyThrows
public static byte[] writeValueAsBytes(Object o) {
return OBJECT_MAPPER.writeValueAsBytes(o);
}
@SneakyThrows
public static <T> T readValue(byte[] bytes, TypeReference<T> typeReference) {
return OBJECT_MAPPER.readValue(bytes, typeReference);
}
@SneakyThrows
public static <T> T readValue(byte[] bytes, Class<T> tClass) {
return OBJECT_MAPPER.readValue(bytes, tClass);
}
@SneakyThrows
public static <T> T readValue(String json, Class<T> tClass) {
return OBJECT_MAPPER.readValue(json, tClass);
}
public static Map<String, Object> flatten(String json, @Nullable String prefix, Boolean handleArray) throws Exception {
JsonNode root = OBJECT_MAPPER.readTree(json);
Map<String, Object> result = new LinkedHashMap();
flatten(prefix == null ? "" : prefix, root, result, handleArray, false);
return result;
}
public static Map<String, Object> flatten(String json) throws Exception {
return flatten(json, "", true);
}
/**
* 打平
* @param prefix
* @param node
* @param map
* @param handleArray
* @param filterNullValue 是否过滤掉 null 值
*/
public static void flatten(String prefix, JsonNode node, Map<String, Object> map, Boolean handleArray, Boolean filterNullValue) {
int nestedLevelSeparatorValueLength = NESTED_LEVEL_SEPARATOR_VALUE != null ? NESTED_LEVEL_SEPARATOR_VALUE.length() : 0;
if (node.isObject()) {
String finalPrefix = prefix;
node.fields().forEachRemaining((e) -> {
JsonNode currentNode = (JsonNode)e.getValue();
flatten(finalPrefix + (String)e.getKey() + NESTED_LEVEL_SEPARATOR_VALUE, currentNode, map, handleArray, filterNullValue);
});
} else if (node.isArray()) {
prefix = prefix.substring(0, prefix.length() - nestedLevelSeparatorValueLength);
if (!handleArray) {
map.put(prefix, node);
} else {
for(int i = 0; i < node.size(); ++i) {
flatten(prefix + ARRAY_ELEMENT_LEFT_FLAG_VALUE + i + ARRAY_ELEMENT_RIGHT_FLAG_VALUE + NESTED_LEVEL_SEPARATOR_VALUE, node.get(i), map, handleArray, filterNullValue);
}
}
} else {
prefix = prefix.substring(0, prefix.length() - nestedLevelSeparatorValueLength);
if(node.isNull() && filterNullValue) {// 过滤值为 null 的节点
//do nothing
} else {
Object value = asSimpleValue(node);
map.put(prefix, value);
}
}
}
private static Object asSimpleValue(JsonNode node) {
if(node.isNull()){//检查当前节点是否为 null (isNull: 判断节点是否为 null, 而非 isEmpty2) , eg: null
return null;
} else if (node.isTextual()) {
return node.textValue();
} else if (node.isNumber()) {
return node.numberValue();
} else {
return node.isBoolean() ? node.booleanValue() : node.asText();
}
}
}
XxxStatusMessageUtils : 根据 json 的 type等 字段决定 反序列化的 Class / ...
//import com.xxxx.dataintegration.common.utils.serialize.JacksonUtils;
import com.alibaba.fastjson2.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.xxx.xxx.protocol.model.common.ExchangeDto;
import com.xxx.xxx.protocol.model.deviceinstruction.DeviceOperationAutoConcrete;
import com.xxx.xxx.protocol.model.deviceinstruction.Xxx.XxxDeviceBmsStatesDto;
import com.xxx.xxx.protocol.model.deviceinstruction.Xxx.XxxDeviceStatesDto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
public class XxxStatusMessageUtils {
private final static Logger log = LoggerFactory.getLogger(DeviceStatusMessageUtils.class);
private final static ObjectMapper objectMapper = new ObjectMapper();
public final static String TRACE_ID_PARAM = "traceId";
public final static String DEVICE_ID_PARAM = "deviceId";
public final static String DATA_PARAM = "data";
public final static String DATA_TYPE_PARAM = "type";//data.type
private static Map<String, Class> typeRegistry = new HashMap<>();//key: "type"属性的值, value: 对应的 class
private static String XXX_DEVICE_STATUS_DTO_TYPE = "XxxDeviceStatesDto";
private static JsonNodeFactory JSON_NODE_FACTORY = JsonNodeFactory.instance;
/**
* typeNode 默认值
*/
private static TextNode DEFAULT_TYPE_NODE = JSON_NODE_FACTORY.textNode( XXX_DEVICE_STATUS_DTO_TYPE );
static {
typeRegistry.put(XXX_DEVICE_STATUS_DTO_TYPE, XxxDeviceStatesDto.class);
typeRegistry.put("XxxDeviceBmsStatesDto", XxxDeviceBmsStatesDto.class);
}
/**
* 反序列化
* @param exchangeDtoJson
* @param errorMessageWhenFail
* @param throwExceptionWhenFail 反序列化失败时,是否抛出异常
* 1. true : 生产环境等正式环境;
* 2. false : 测试环境、或临时处理脏数据,防止程序持续不通过(例如: Deserialize to exchange dto fail!KafkaLogTraceId:KafkaLogTraceId(offset=27295, partition=0, timestamp=1760685656444, ingestTime=1760689997548)Deserialize to ExchangeDto fail!exception.message:Cannot deserialize value of type `com.xxx.xxx.protocol.protobuf.Xxx$DeviceState$XxxInfo$SystemMode` from String "UNKNOWN": not one of the values accepted for Enum class: [SYSTEM_MODE_IDLE, SYSTEM_MODE_BUSY, UNRECOGNIZED])
* @return
*/
public static ExchangeDto deserialize(String exchangeDtoJson, String errorMessageWhenFail,Boolean throwExceptionWhenFail) {
ExchangeDto exchangeDto = null;
String traceId = null;
String deviceId = null;
try {
//ExchangeDto exchangeDto = JSON.parseObject( exchangeDtoJson, ExchangeDto.class );//fastjson 方式1,将报错
//ExchangeDto exchangeDto = objectMapper.convertValue( exchangeDtoJson, ExchangeDto.class );//jackson 方式2,将报错
//ExchangeDto exchangeDto = JacksonUtils.readValue(exchangeDtoJson, ExchangeDto.class); //Xxx 中台提供的方式3,将报错
//方式4 根据特殊诉求进行反序列化
//ObjectMapper mapper = new ObjectMapper();
// 1. 先整体读成树
JsonNode root = mapper.readTree( exchangeDtoJson );
traceId = root.get(TRACE_ID_PARAM).asText();
deviceId = root.get(DEVICE_ID_PARAM).asText();
// 2. 读 data 节点
JsonNode dataNode = root.get(DATA_PARAM);
//JsonNode DeviceStateNode = dataNode.get("DeviceState");
// 3. 根据 type 字段动态决定 具体的 Dto Class (利用了 { XxxOperationAutoConcrete } 基于 @JsonTypeInfo( use = Id.NAME, property = "type", visible = true ) (等效于 StdTypeResolverBuilder) 的 jackson 多态机制 )
JsonNode typeNode = dataNode.get(DATA_TYPE_PARAM);
String type = null;
if(typeNode == null){//json 中无 type 字段时,设置默认情况
/**
* @note
* 解决因json中取消了 `type` = 'XxxDeviceStatesDto' 字段,在本组件反序列化时
* `Object realData = mapper.treeToValue(dataNode, clazz);` 报错:
* Missing type id when trying to resolve subtype of [simple type, class com.xxx.XxxDeviceStatesDto]: missing type id property 'type'
*/
ObjectNode objectDataNode = (ObjectNode) dataNode;// JsonNode (只可读) 转 ObjectNode (可读可写)
objectDataNode.set(DATA_TYPE_PARAM, DEFAULT_TYPE_NODE);
type = XXX_DEVICE_STATUS_DTO_TYPE;
} else {
type = typeNode.asText();
}
Class<?> clazz = typeRegistry.get(type);// 自己维护 Map<String,Class<?>>
// 4. 二次反序列化
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);//避免因未知属性: `type`而报错, Method threw 'com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException' exception.
mapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);//匹配不到的枚举值,直接赋 null。避免因枚举值被移除或不存在,而报错: "com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `com.xxx.xxx.protocol.protobuf.Xxx$DeviceState$XxxInfo$SystemMode` from String "UNKNOWN": not one of the values accepted for Enum class: [SYSTEM_MODE_IDLE, SYSTEM_MODE_BUSY, UNRECOGNIZED]"
Object realData = mapper.treeToValue(dataNode, clazz);// realData 已是强类型对象
exchangeDto = new ExchangeDto();
exchangeDto.setTraceId( traceId );
exchangeDto.setDeviceId( deviceId );
exchangeDto.setData( (DeviceOperationAutoConcrete) realData);
} catch ( JsonProcessingException exception) {
String errorMessage = ( (errorMessageWhenFail==null)?"":errorMessageWhenFail ) + String.format(
"Deserialize to ExchangeDto fail!exception.message:%s, traceId:%s, deviceId:%s, exchangeDtoJson:%s"
, exception.getMessage(), traceId, deviceId, exchangeDtoJson
);
log.error(errorMessage + ", exception:", exception);
if(throwExceptionWhenFail){
throw new RuntimeException( errorMessage, exception);
}
}
return exchangeDto;
}
}
Y 推荐文献
- Jackson
- 史上最全的Jackson框架使用教程 - CSDN //TODO
- Jackson教程 - baeldung-cn.com //todo
- Java之Jackson使用详解 - Zhihu//todo
X 参考文献
本文链接: https://www.cnblogs.com/johnnyzen
关于博文:评论和私信会在第一时间回复,或直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
日常交流:大数据与软件开发-QQ交流群: 774386015 【入群二维码】参见左下角。您的支持、鼓励是博主技术写作的重要动力!

浙公网安备 33010602011771号