• 记一次 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);
  • 相关阅读:
    1111实验二 作业调度模拟实验
    1006实验一实验报告
    0909对操作系统的认识
    南阳OJ-138 找球号(二)(hash表应用)
    南阳OJ-38 布线问题(最小生成树应用_prim)
    插入排序
    南阳OJ-756 重建二叉树(二叉树的中序遍历和后序遍历求先序遍历)
    南阳OJ-63 小猴子下落(数据结构-二叉树)
    UVA OJ-11095 Maximum Product(暴力求解法)
    UVA OJ-725 Division (暴力求解法)
  • 原文地址:https://www.cnblogs.com/siweipancc/p/json-cast-to-java-ClassCastException-java-util-LinkedHashMap-cannot-be-cast-to-Entity.html
Copyright © 2020-2023  润新知