ObjectMapper自定义反序列化器遇到的问题记录 您所在的位置:网站首页 英语idcard怎么画 ObjectMapper自定义反序列化器遇到的问题记录

ObjectMapper自定义反序列化器遇到的问题记录

2024-04-04 09:55| 来源: 网络整理| 查看: 265

发现问题

因项目里面同时存在fastjson与jackson两种Json处理的框架,部分对象中包含了fastjson的JSONObject类,反/序列化对象时使用的是jackson,现因一些情况需要将JSONObject为{}(空对象)时变成null返回。

则定义了以下的方法:

public class MainTest { public static void main(String[] args) throws JsonProcessingException { JSONObject otherInfoForNull = null; JSONObject otherInfo = new JSONObject(); TestDTO dto = TestDTO.builder().name("小明").age(12).sex("男").address("光明小区").birthday("2015-05-14").otherInfo(otherInfoForNull).build(); String json = objectMapper().writeValueAsString(dto); TestDTO testDTO = objectMapper().readValue(json, TestDTO.class); System.out.println(testDTO); } static ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); //忽略空Bean转json的错误 mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); //忽略 在json字符串中存在,但是在java对象中不存在对应属性的情况。防止错误 mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); //注册序列化提供能力的对象s SimpleModule simpleModule = new SimpleModule(); // JsonObject simpleModule.addDeserializer(JSONObject.class, new JSONObjectDeserializer()); mapper.registerModule(simpleModule); return mapper; } } /** * 反序列化JSONObject */ class JSONObjectDeserializer extends JsonDeserializer { @Getter private final JSONObject defaultValue; public JSONObjectDeserializer() { this(null); } public JSONObjectDeserializer(JSONObject defaultValue) { this.defaultValue = defaultValue; } @Override public JSONObject deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { String value = p.getValueAsString(); if (StringUtils.isBlank(value)) { return this.defaultValue; } JSONObject result = JSON.parseObject(value); return MapUtils.isEmpty(result) ? this.defaultValue : result; } } @Data @NoArgsConstructor @AllArgsConstructor @Builder(toBuilder = true) class TestDTO { private String name; private Integer age; private String sex; private JSONObject otherInfo; private String address; private String birthday; }

最开始的个人理解:

定义一个该类型的反序列化器jackson判断属性类型为JSONObject后会拿到该字段的一些上下文或字段内容来调用自定义的反序列化器

乍一看没有什么问题,来运行一下:

当otherInfo的值设置为otherInfoForNull时,运行如下: TestDTO(name=小明, age=12, sex=男, otherInfo=null, address=光明小区, birthday=2015-05-14)当otherInfo的值设置为otherInfo时,运行如下: TestDTO(name=小明, age=12, sex=男, otherInfo=null, address=null, birthday=null)

此时发现address、birthday的值都是空的,说明反序列化器写的有问题,这时候就需要从deserialize方法的入参着手排查下了。

问题探究 DeserializationContext

用于在反序列化过程中提供上下文信息和功能。为JsonDeserializer提供了访问和操作反序列化过程中的各种上下文环境的方法。

可以获取配置信息,如日期格式、空值处理等配置可以创建新的Java对象实例,并将反序列化的数据填充到该对象中…

这个对象不是重点,下面看一下JsonParser

JsonParser

Json解析器作用是从JSON字符串中提取出符合JSON格式要求的数据,并将其传递给后续的JsonProcessor或其他Json工具类进行进一步处理。可以编写自定义的JSON解析器,以满足不同的需求。自定义的过程可以通过实现JsonFactory中的createJsonParser方法来实现。个人感觉解析过程有点像编译原理的词法分析。

其中比较常用的方法:

词单元相关 nextToken()currentToken()… 值相关 getText()getIntValue()getShortValue()getLongValue()… 字段名相关 getCurrentName()nextFieldName()… public static void main(String[] args) throws IOException { JSONObject otherInfo = null; TestDTO dto = TestDTO.builder().name("小明").age(12).sex("男").address("光明小区").birthday("2015-05-14").otherInfo(otherInfo).build(); String json = objectMapper().writeValueAsString(dto); JsonFactory jsonFactory = new JsonFactory(); JsonParser parser = jsonFactory.createParser(json); while (!parser.isClosed()) { JsonToken jsonToken = parser.nextToken(); if (null == jsonToken) { continue; } System.out.printf("currentName: %s \t type: %s \t value: %s\n", parser.getCurrentName(), jsonToken.name(), parser.getText()); } }

输出:

currentName: null type: START_OBJECT value: { currentName: name type: FIELD_NAME value: name currentName: name type: VALUE_STRING value: 小明 currentName: age type: FIELD_NAME value: age currentName: age type: VALUE_NUMBER_INT value: 12 currentName: sex type: FIELD_NAME value: sex currentName: sex type: VALUE_STRING value: 男 currentName: otherInfo type: FIELD_NAME value: otherInfo currentName: otherInfo type: VALUE_NULL value: null currentName: address type: FIELD_NAME value: address currentName: address type: VALUE_STRING value: 光明小区 currentName: birthday type: FIELD_NAME value: birthday currentName: birthday type: VALUE_STRING value: 2015-05-14 currentName: null type: END_OBJECT value: }

可以看到Json被拆解了,需要通过Token判断类型来获取字段名以及值。可以看下简单的用法

public static void main(String[] args) throws IOException { JSONObject otherInfo = buildJSONObject(); TestDTO dto = TestDTO.builder().name("小明").age(12).sex("男").address("光明小区").birthday("2015-05-14").otherInfo(otherInfo).build(); String json = objectMapper().writeValueAsString(dto); JsonFactory jsonFactory = new JsonFactory(); JsonParser parser = jsonFactory.createParser(json); print(parser, "", -1, false); } // 初始化数据 public static JSONObject buildJSONObject() { JSONObject otherInfo = new JSONObject(); // 年级 List gradeInfoList = new ArrayList(); gradeInfoList.add(GradeInfo.builder().name("一年级").teacherList(Arrays.asList( TeacherInfo.builder().name("张老师").type("数学").idCard(IdCard.builder().number("123456789").build()).build(), TeacherInfo.builder().name("王老师").type("语文").idCard(IdCard.builder().number("987654321").build()).build()) ).build()); gradeInfoList.add(GradeInfo.builder().name("三年级").teacherList(Collections.singletonList( TeacherInfo.builder().name("刘老师").type("英语").idCard(IdCard.builder().number("753951468").build()).build()) ).build()); otherInfo.put("gradeInfoList", gradeInfoList); otherInfo.put("idCard", IdCard.builder().number("0112244").build()); return otherInfo; } public static void print(JsonParser parser, String name, int arrayIndex, boolean isArray) throws IOException { while (!parser.isClosed()) { JsonToken jsonToken = parser.nextToken(); // 因为存在递归调用,需要给一个退出条件(token为空 || 对象结束 || 数组结束)时需要退出递归 if (jsonToken == null || JsonToken.END_OBJECT.equals(jsonToken) || JsonToken.END_ARRAY.equals(jsonToken)) { return; } String currentName = name; if (JsonToken.FIELD_NAME.equals(jsonToken)) { parser.nextToken(); currentName = buildName(name, arrayIndex, parser); } if (parser.isExpectedStartObjectToken()) { print(parser, currentName, isArray ? arrayIndex++ : -1, false); continue; } if (parser.isExpectedStartArrayToken()) { print(parser, currentName, 0, true); continue; } String value = parser.getValueAsString(); System.out.printf("%s:%s\n", currentName, value); } } // 构建名称 private static String buildName(String name, int arrayIndex, JsonParser parser) throws IOException { String currentName = parser.getCurrentName(); if (StringUtils.isBlank(name)) { return currentName; } // 数组 if (arrayIndex >= 0) { return name + "[" + arrayIndex + "] -> " + currentName; } // 对象 return name + " -> " + currentName; }

输出:

name:小明 age:12 sex:男 otherInfo -> idCard -> number:0112244 otherInfo -> gradeInfoList[0] -> name:一年级 otherInfo -> gradeInfoList[0] -> teacherList[0] -> name:张老师 otherInfo -> gradeInfoList[0] -> teacherList[0] -> type:数学 otherInfo -> gradeInfoList[0] -> teacherList[0] -> idCard -> number:123456789 otherInfo -> gradeInfoList[0] -> teacherList[1] -> name:王老师 otherInfo -> gradeInfoList[0] -> teacherList[1] -> type:语文 otherInfo -> gradeInfoList[0] -> teacherList[1] -> idCard -> number:987654321 otherInfo -> gradeInfoList[1] -> name:三年级 otherInfo -> gradeInfoList[1] -> teacherList[0] -> name:刘老师 otherInfo -> gradeInfoList[1] -> teacherList[0] -> type:英语 otherInfo -> gradeInfoList[1] -> teacherList[0] -> idCard -> number:753951468 address:光明小区 birthday:2015-05-14 问题解决

这样看下来,仅仅使用JsonParser的getValueAsString()方法是没有办法获取到当前完整json的,但也不用像上面那样复杂的去解析,jackson有另一种方法可以直接拿到完整json。

static class JSONObjectDeserializer extends JsonDeserializer { @Getter private final JSONObject defaultValue; public JSONObjectDeserializer() { this(null); } public JSONObjectDeserializer(JSONObject defaultValue) { this.defaultValue = defaultValue; } @Override public JSONObject deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { // 重点拿到json树转为string!! JsonNode treeNode = p.getCodec().readTree(p); String value = treeNode.toString(); if (StringUtils.isBlank(value)) { return this.defaultValue; } JSONObject result = JSON.parseObject(value); return MapUtils.isEmpty(result) ? this.defaultValue : result; } }

将反序列化方法改为上面的即可解决问题。

反思

出现这种问题主要是自己对jackson框架了解不深,该框架功能非常强大,不能停留在简单的使用,需要进一步提高自身的熟练程度。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有