jackson:无法将java.util.LinkedHashMap强制转换为X

JsonJack:无法将java.util.LinkedHashMap强制转换为X

1.概述

Jackson是一个广泛使用的Java库,它使可以方便地对JSON或XML进行序列化/反序列化。

有时,当尝试将JSON或XML反序列化为对象集合时,可能会遇到“ java.lang.ClassCastException:java.util.LinkedHashMap无法转换为X”。

2.了解问题

让创建一个简单的Java应用程序来重现此异常,以了解何时会发生该异常。

2.1创建一个POJO类

public class Book {
    private Integer bookId;
    private String title;
    private String author;
    //getters, setters, constructors, equals and hashcode omitted
}

json数据

[ {
    
    
    "bookId" : 1,
    "title" : "A Song of Ice and Fire",
    "author" : "George R. R. Martin"
}, {
    
    
    "bookId" : 2,
    "title" : "The Hitchhiker's Guide to the Galaxy",
    "author" : "Douglas Adams"
}, {
    
    
    "bookId" : 3,
    "title" : "Hackers And Painters",
    "author" : "Paul Graham"
} ]
   ObjectMapper objectMapper = new ObjectMapper();
    List<Book> bookList = objectMapper.readValue(jsonString, ArrayList.class);
    System.out.println("test1:"+bookList);
    System.out.println( bookList.get(0).getBookId());//这步出现问题

现在,如果仔细查看异常消息:“无法将类java.util.LinkedHashMap强制转换为类… Book”,可能会出现两个问题。

已经声明了类型为List 的bookList变量,但是为什么Jackson尝试将LinkedHashMap类型转换为Book类呢?此外,LinkedHashMap来自何处?

首先,实际上声明了类型为List 的bookList。但是,当我们调用objectMapper.readValue()方法时,我们将ArrayList.class传递为Class对象。因此,Jackson会将JSON内容反序列化为ArrayList对象,但不知道ArrayList对象中应该包含哪种类型的元素。

其次,当Jackson尝试在JSON中反序列化对象时,但未提供目标类型信息时,它将使用默认类型:LinkedHashMap。换句话说,反序列化之后,我们将获得一个ArrayList 对象。在地图中,键是属性的名称,例如“ bookId”,“ title”等。这些值是相应属性的值:

在这里插入图片描述

3.将TypeReference传递给objectMapper.readValue()

要解决该问题,需要以某种方式让Jackson知道元素的类型。 但是,编译器不允许我们执行诸如objectMapper.readValue(jsonString,ArrayList .class)之类的操作。

相反,可以将TypeReference对象传递给objectMapper.readValue(String content,TypeReference valueTypeRef)方法。 在这种情况下,我们只需要传递新的TypeReference <List >(){}作为第二个参数:

ObjectMapper objectMapper = new ObjectMapper();
List<Book> bookList = objectMapper.readValue(jsonString, new TypeReference<List<Book>>() {
    
    });
System.out.println("test2:"+bookList);
  System.out.println( bookList.get(0).getBookId());

4.将JavaType传递给objectMapper.readValue()

在上一节中,传递Class对象或TypeReference对象作为第二个参数来调用objectMapper.readValue()方法。

objectMapper.readValue()方法仍然接受JavaType对象作为第二个参数。 JavaType是类型标记类的基类。 反序列化器将使用它,以便反序列化器知道反序列化过程中的目标类型。

可以通过TypeFactory实例构造一个JavaType对象,并且可以从objectMapper.getTypeFactory()中检索TypeFactory对象。

在此示例中,要具有的目标类型是ArrayList 。 因此,我们可以按照以下要求构造一个CollectionType:

objectMapper.getTypeFactory().constructCollectionType(ArrayList.class, Book.class);
 ObjectMapper objectMapper = new ObjectMapper();
 CollectionType listType  = objectMapper.getTypeFactory().constructCollectionType(ArrayList.class, Book.class);
 List<Book> bookList = objectMapper.readValue(jsonString, listType);
 System.out.println("test4:"+bookList);
 System.out.println( bookList.get(0).getBookId());

5.使用JsonNode对象和objectMapper.convertValue()方法

上面已经看到了将TypeReference或JavaType对象传递给objectMapper.readValue()方法的解决方案。

另外,可以在Jackson中使用树模型节点,然后通过调用objectMapper.convertValue()方法将JsonNode对象转换为所需的类型。

同样,可以将TypeReference或JavaType对象传递给objectMapper.convertValue()方法。

ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(jsonString);
List<Book> bookList = objectMapper.convertValue(jsonNode,
objectMapper.getTypeFactory().constructCollectionType(ArrayList.class, Book.class));
System.out.println("test2:"+bookList);
System.out.println( bookList.get(0).getBookId());

6.创建通用的反序列化方法

到目前为止,已经解决了在将JSON数组反序列化为Java集合时如何解决类转换问题。 在现实世界中,可能想创建一个通用方法来处理不同的元素类型。

可以在调用objectMapper.readValue()方法时传递一个JavaType对象:

  public static <T> List<T> jsonArrayToList(String json, Class<T> elementClass) throws IOException {
    
    
        ObjectMapper objectMapper = new ObjectMapper();
        CollectionType listType =
                objectMapper.getTypeFactory().constructCollectionType(ArrayList.class, elementClass);
        return objectMapper.readValue(json, listType);
    }

为什么不使用TypeReference方法来构建通用方法,因为它看起来更紧凑?

现在,创建一个通用实用程序方法,并将相应的TypeReference对象传递给objectMapper.readValue()方法:

 public static <T> List<T> jsonArrayToList(String json, Class<T> elementClass) throws IOException {
    
    
        return new ObjectMapper().readValue(json, new TypeReference<List<T>>() {
    
    });
    }

运行结果:

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.example.demo.web.Testb$Book

将TypeReference对象传递给readValue()方法,并且以前已经看到这种方法可以解决类转换问题。 那么,为什么在这种情况下会看到相同的异常?

这方法是通用的。 即使使用类型参数T传递TypeReference实例,也无法在运行时对类型参数T进行验证。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/niugang0920/article/details/115251513