【问题标题】:Spring Boot using Jackson and an external Library with GsonSpring Boot 使用 Jackson 和带有 Gson 的外部库
【发布时间】:2022-01-04 17:03:46
【问题描述】:

我正在使用带有 Jackson 的 Spring Boot 应用程序(版本 2.4.5)。我可以添加一个使用 gson 的胖 jar 而不影响应用程序的 Jackson 反序列化吗?

当我将 jar 添加到我的应用程序时,应用程序在任何地方都使用 gson 来反序列化 JSON。但是由于我使用的是 Jackson 的一些特殊功能,所以失败了。

很遗憾,我无法在库中更改它。

我使用 Gradle 在依赖项中添加这样的 fat jar:

implementation fileTree(dir: 'libs', include: ['*.jar'])

我正在构建 Jacksons 对象映射器,例如:

private static ObjectMapper getMapper() {
  ObjectMapper mapper = new ObjectMapper();
  mapper.registerModule(new Jdk8Module());
  mapper.registerModule(new JavaTimeModule());

  return mapper;
}

以下行会引发错误:

List<User> users = objectMapper.readValue(getReader("users.json"), new TypeReference<>() {});
java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class User (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; onek.books.backend.model.book.Book is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @445d3853)

并且 Gson 被添加到具有以下 maven 依赖项的 fat jar 中:

<dependency>
  <groupId>com.google.code.gson</groupId>
  <artifactId>gson</artifactId>
  <version>${gson-version}</version>
</dependency>
<dependency>
  <groupId>io.gsonfire</groupId>
  <artifactId>gson-fire</artifactId>
  <version>${gson-fire-version}</version>
</dependency>

编辑

正如建议的那样,这不是由于 gson 我试图缩小范围并一一添加所有依赖项,并且没有一个本身导致错误。

但我可能找到了原因,但我不知道如何解决这个问题。这是 Jacksons typeFactory 构造它的类型的方式。对于线路:

List<Book> books = objectMapper.readValue(getReader("books.json"), new TypeReference<>() {});

如果 jar 不存在,则构造的 Type 如下所示:

result = {CollectionType@13667} "[collection type; class java.util.List, contains [simple type, class onek.books.backend.model.book.Book]]"
 _elementType = {SimpleType@13669} "[simple type, class onek.books.backend.model.book.Book]"
 _superClass = null
 _superInterfaces = {JavaType[1]@13670} 
 _bindings = {TypeBindings@13671} "<Lonek/books/backend/model/book/Book;>"
 _canonicalName = null
 _class = {Class@352} "interface java.util.List"
 _hash = -95724352
 _valueHandler = null
 _typeHandler = null
 _asStatic = false

这是我所期望的,因为 Book-Class 在类型中。但是,如果我添加 jar,则构造的 Type 看起来像:

result = {SimpleType@11757} "[simple type, class java.lang.Object]"
 _superClass = null
 _superInterfaces = null
 _bindings = {TypeBindings@11759} "<>"
 _canonicalName = null
 _class = {Class@595} "class java.lang.Object"
 _hash = 1063877011
 _valueHandler = null
 _typeHandler = null
 _asStatic = false

jackson 将其反序列化为 LinkedHashmap,这种类型会导致 ClassCastException。

但我真的不知道如何从这里继续。外部 jar 可以更改 Jackson TypeFactory 的某些部分吗?

【问题讨论】:

  • 根据您提供的代码 sn-p,我不清楚这与 Gson 有什么关系。您明确调用杰克逊的ObjectMapper 中的readValue; Gson 应该对此没有影响。
  • 是的,我自己也是这么想的。但很明显,这个带有 gson 的 jar 会导致问题,因为该行没有它但不能使用它。
  • @Marcono1234 你完全正确。依赖没有任何作用(请参阅我的编辑)。但不知何故,jar 改变了类型构造的结果。

标签: java spring-boot jackson gson


【解决方案1】:

问题很可能在于“胖 JAR”的创建。

在创建fat JAR时要注意以下几点:

  • 依赖项提供的META-INF/MANIFEST.MF 应该被忽略
  • 依赖提供的module-info.classMETA-INF/versions/<i>&lt;version&gt;</i>/module-info.class应该被忽略(后者被Multi-Release JARs使用)
  • META-INF/services/下的文件内容应该合并(他们声明providers for services
  • 可选:如果其中一个依赖项是 Multi-Release JARs,您可以在胖 JAR 的 MANIFEST.MF 中设置 Multi-Release: true,以允许 JRE 使用这些特定于版本的类
  • 对于所有其他文件,如何处理冲突可能是特定于应用程序的

如果您使用插件来生成胖 JAR,那么它可能会为这些转换提供设置(或者可能会默认执行它们)。

【讨论】:

    【解决方案2】:

    当 Jackson 尝试反序列化未提供类型信息的对象时,它使用 LinkedHashMap 来存储对象属性值。异常可能与此有关。

    我的猜测是,胖 jar 可能包含与您在项目中包含的版本不同的 Jackson 版本,并且由于某种原因这种行为不同。

    我不知道这是否只是问题的不准确,但为了解决问题,请尝试在您的 TypeReference 中提供实际类型:

    List<Book> books = objectMapper.readValue(getReader("books.json"), new TypeReference<List<Book>>() {});
    

    请考虑阅读this related articleSO question

    【讨论】:

    • 谢谢,这正是正在发生的事情。 jar 依赖项中有一个旧版本的 Jackson。我设法用与服务器相同的版本构建了 jar,它现在可以工作了。我无法像您建议的那样更改 TypeReference。这只会引发其他错误。
    猜你喜欢
    • 2015-01-07
    • 1970-01-01
    • 2017-04-08
    • 2018-01-25
    • 2016-05-07
    • 2018-09-16
    • 2020-10-01
    • 2023-02-24
    • 1970-01-01
    相关资源
    最近更新 更多