zoukankan      html  css  js  c++  java
  • 记一次 Json 对象转换为 Java 对象的问题

    1、描述

    最近在使用 Jackson 将 Json 串转换回 Java 对象的时候遇到了 ClassCastException 错误,特此记述。

    2、问题复现

    问题出现的节点在于属性节点的 JavaType 不明确,比如使用了泛型 和 Object,如下:

     1     @Getter
     2     @Setter
     3     @NoArgsConstructor
     4     @AllArgsConstructor
     5     private static class JsonResult<T> {
     6         private String message;
     7         private T result; // 问题出现在这里
     8     }
     9 
    10     @Builder
    11     @Getter
    12     @Setter
    13     @NoArgsConstructor
    14     @AllArgsConstructor
    15     private static class Container {
    16         private String key;
    17         private String value;
    18     }

    使用如下的测试的用例

     1     @Test
     2     void testWrite() throws JsonProcessingException {
     3         final ObjectMapper mapper = new ObjectMapper();
     4         mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
     5         final JsonResult<Container> resultBefore = new JsonResult<>();
     6         resultBefore.setMessage("foo");
     7         final Container containerBefore = Container.builder().key("key").value("value").build();
     8         resultBefore.setResult(containerBefore);
     9         final String writtenString = mapper.writeValueAsString(resultBefore);
    10         //----------------read str as object
    11         final JsonResult parsedResult = mapper.readValue(writtenString, JsonResult.class);
    12         assert parsedResult != null; // 之后将在这里打断点
    13 
    14         final Container container = (Container) parsedResult.result;
    15         assertThat(container).as("not null").isNotNull().extracting(Container::getKey).as("key equal").isEqualTo(containerBefore.key);
    16         assertThat(container).extracting(Container::getValue).as("key equal").isEqualTo(containerBefore.value);
    17     }
    18 }

    将会得到错误: java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to Container

    3、问题解析

    让我们在测试中打上断点,看看经由 jackson 反序列化后的对象内容

      可以看到这时候 Container 类的内容在这里变成了 hashMap,其实各种原因不难理解,在序列化跟反序列化中,我们给 Jackson 传递的 Class 是 JsonResult,回到相应类的定义,会发现我们使用的是泛型,Jackson 并不知道 rsult 中实际存放的类型,对 Object 也是如此(Object 是所有非原始类型的祖先)

      在 ide 的提示里,我们也可以看到这时候 parsedResult 里边的 result 实际上是 Object 类型

    4、思路与解决方式

    4.1 思路

      从上边的分析来,看解决的思路很简单,我们需要告诉 Jackson  result 中存放的数据类型。方法有二

    4.2 解决方式

    4.2.1 改变反序列化时传递的 Class 参数

    4.2.1.1 泛型

      当对象有泛型参数时候,我们只要构建一个新类型,让它继承原本的类并指定泛型参数即可。在原本的代码中,我们加入。 

    1 private static class ContainerJsonRsult extends JsonResult<Container>{}

      并且改变相应的反序列化语句即可。

    final ContainerJsonRsult parsedResult = mapper.readValue(writtenString, ContainerJsonRsult.class);

    4.2.1.2 Object

      当对象的没有泛型签名时,我们需要构建一个新类型,让他继承原本的类,并让他拥有目标 Containner 类型的同名参数 result

    1     @Getter
    2     @Setter
    3     @NoArgsConstructor
    4     @AllArgsConstructor
    5     private static class ContainerJsonResult extends JsonResult {
    6         private Container result;
    7     }

      然后修改对应的反序列语句:

    1 final ContainerJsonResult parsedResult = mapper.readValue(writtenString, ContainerJsonResult.class);

    2、在反序列化中手动传递 result 对应的 Calss 类型

    对于存在泛型的类,推荐 4.2.1.1 的解决方式,当 rsult 中指向的是 Object 或者 T  类型时,都可以指定相应的 Class 类进行二次转换:

    1  Container container = mapper.convertValue(parsedResult.getResult(), Container.class);

      或者

    1         JavaType type = mapper.getTypeFactory().constructType(Container.class);
    2         Container container = mapper.convertValue(parsedResult.result, type);
  • 相关阅读:
    XMPP框架 微信项目开发之XMPP配置——MySQL数据库、MySQLworkbench、Openfire服务器的安装与配置
    Mac Mysql 启动关闭和重启命令、重新设置root密码 、 卸载
    CocoaPods安装使用 关键点
    CocoaPods的介绍、安装、使用和原理
    iOS 组件化架构漫谈
    将自己库添加Cocoapods支持
    Appium移动端自动化测试-安卓真机+模拟器启动
    Java学习第二十五天
    Java学习第二十四天
    Java学习第二十三天
  • 原文地址:https://www.cnblogs.com/siweipancc/p/json-cast-to-java-ClassCastException-java-util-LinkedHashMap-cannot-be-cast-to-Entity.html
Copyright © 2011-2022 走看看