0%

Java反序列化之DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES

Java 反序列化

private static final ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES属性默认为true开启状态,当属性为true时,表示在反序列化时遇到未知属性(属性没有对应的类属性来映射,并且没有任何setter或handler来处理这样的属性)时会引起结果失败(通过抛JsonMappingException异
忽略不需要的字体
有时候,返回的JSON字符串中含有我们并不需要的字段,那么当对应的实体类中不含有该字段时,会抛出一个异常,告诉你有些字段没有在实体类中找到。解决办法很简单,在声明ObjectMapper之后,加上下述代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private static final ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

public static Map<String, ExProduct> getExProduct(RedisService redisService) {
Map<String, String> productJson = redisService.getMap("HRY:EXCHANGE:PRODUCT");
Map<String, ExProduct> map = new HashMap<>();
productJson.forEach((k, v) -> {
try {
ExProduct product = mapper.readValue(v, ExProduct.class);
map.put(k, product);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
return map;
}

补充

对于结构固定的JSON,使用ObjectMapper结合某个预先定义的实体类型可以非常方便地完成反序列化工作,比如对下面这样的JSON:

GET /person/1

{
“id”: “1”,
“name”: “dm_vincent”,
“age”: “28”
}

结合一个实体类型,可以很轻松的完成反序列化工作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Person {
public long id;
public String name;
public int age;
}
public static <T> T getEntity(String jsonString, Class<T> prototype) {

ObjectMapper objectMapper = new ObjectMapper();
try {
return (T) objectMapper.readValue(jsonString, prototype);
} catch (IOException e) {
e.printStackTrace();
}

return null;
}

但是在某些支持一次性获取多条记录的API中,就出现问题了。比如拥有下面这种格式的API:

GET /person/1,2,3

{
“dm_vincent”: {
“id”: “1”,
“name”: “dm_vincent”,
“age”: “28”
},
“dm_vincent2”: {
“id”: “2”,
“name”: “dm_vincent2”,
“age”: “29”
},
“dm_vincent3”: {
“id”: “3”,
“name”: “dm_vincent3”,
“age”: “30”
}
}
虽然需要获取的实体类型还是那个Person类,可是这个时候就不像上面那样简单明了了。比如下面这段代码在反序列化的时候会出现问题:

1
2
3
public class PersonWrapper {
public Map<String, Person> persons;
}

我们的意图是明确的,将返回的多个Person实体对象放到一个Map结构中。但是问题就在于返回的JSON中的keys是不固定的(比如上述JSON中的keys是人名),这导致反序列化失败。毕竟默认配置下的ObjectMapper也没有聪明到这种程度,能够猜测你是想要将多个实体放到Map中。

正确的做法之一是使用ObjectMapper的readTree方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static <T> EntityWrapper<T> getEntityWrapper(String jsonString, Class<T> prototype) {
ObjectMapper objectMapper = new ObjectMapper();
EntityWrapper<T> wrapper = new EntityWrapper<T>();
try {
JsonNode root = objectMapper.readTree(jsonString);
Iterator<Entry<String, JsonNode>> elements = root.fields();

while (elements.hasNext()) {
Entry<String, JsonNode> node = elements.next();
String key = node.getKey();
T element = objectMapper.readValue(node.getValue().toString(), prototype);
wrapper.addEntry(key, element);
}

return wrapper;
} catch (IOException e) {
e.printStackTrace();
}

return null;
}

简单解释一下上述代码:
使用root.field()方法能够得到返回的JSON中的所有key-value对。
然后循环提取某个键值对的key和value,对于value我们可以直接使用之前的策略进行反序列化,因为这部分的结构也是固定的。

欣赏此文?求鼓励,求支持!