微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

反序列化 Jackson 中的嵌套对象

如何解决反序列化 Jackson 中的嵌套对象

根据我所看到的文档和示例,这应该是直接的,但我还没有设法让它工作。如果此问题已发布在其他地方,我深表歉意,但即使它与使用 Jackson 库的嵌套对象反序列化有关,我也无法找到相同的问题。

关键项目:

  • Jackson 解析器试图将嵌套对象映射到 LinkedHashMap 而不是实际的对象/类类型
  • 使用第 3 方类,因此无法进行注释
  • JSON 数据不可修改,因为它是在别处生成

基本上,我的 JSON 是从 GCP 日志生成的,看起来像这样(为了便于阅读而进行了简化):

{
  "logName": "projects/my_project_id/logs/stdout","severity": "ERROR","timestamp": "2021-07-25T01:32:56.371417530Z","receiveTimestamp": "2021-07-25T01:33:01.118327598Z","jsonPayload": {
    "message": "application log message"
  },"resource": {
    "type": "gce_instance","labels": {
      "project_id": "my_project_id","instance_id": "my_instance_id","zone": "my_zone"
    }
  },"operation": {
    "first": true,"id": "<operation_id>","producer": "producer_name"
  }
}

使用 Jackson 数据绑定库,我试图将其解析并反序列化为 Java 对象。

当我使用 GCP 时,Google 为这些日志对象提供了库。即 com.google.api.services.logging.v2.model.LogEntry 和它的相关对象,如 HttpRequest、LogEntryOperation 等。这个对象主要有简单的对象,如 String、Integer 和 Maps,但也有一些是上面提到的对象(HttpRequest 、LogEntryOperation 等)。

例如

public final class LogEntry extends GenericJson {

    private HttpRequest httpRequest;

    private String insertId;

    private Map<String,Object> jsonPayload;

    private Map<String,String> labels;

    private String logName;

    private MonitoredResourceMetadata Metadata;

    private LogEntryOperation operation;

    ...
}

从我看到的文档和示例来看,使用 readValue 方法并将我想要映射到的类传递给它应该非常简单:

byte[] data = jsonString.getBytes();
ObjectMapper mapper = new ObjectMapper();
mapper.readValue(new ByteArrayInputStream(data),LogEntry.class);

但是我收到此错误

com.fasterxml.jackson.databind.JsonMappingException: Can not set com.google.api.services.logging.v2.model.LogEntryOperation field com.google.api.services.logging.v2.model.LogEntry.operation to java.util.LinkedHashMap (through reference chain: com.google.api.services.logging.v2.model.LogEntry["operation"])

对于 LogEntry 类中的每个嵌套对象都会发生这种情况。只是为了确认我克隆了 LogEntry 类并将这些对象(例如 LogEntryOperation)更新为 Map 并且它能够工作。

现在的问题是我不想克隆这个类,因为它是库的一部分,我也无法更改 GCP 中的数据。

我还尝试对 ObjectMapper 使用配置(SerializationFeature 和 DeserializationFeature),但我找不到任何有用的东西。我假设当您将类作为参数传递时,Jackson 解析器能够确定嵌套对象。

我是否缺少配置或其他什么东西?或者使用 Jackson 解析器无法做到这一点?

谢谢!

编辑 1:完整的堆栈跟踪:

com.abc.logging.data.parsing.LogEntryParser$LogParseException: Failed to parse class com.google.api.services.logging.v2.model.LogEntry value from json payload
    at com.abc.logging.data.parsing.LogEntryParser.parse(LogEntryParser.java:87)
    at com.abc.logging.data.parsing.LogEntryParser.parse(LogEntryParser.java:34)
    at com.abc.logging.data.parsing.LogEntryParserTest.testMapper(LogEntryParserTest.java:191)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
    at org.junit.runners.BlockJUnit4ClassRunner.runchild(BlockJUnit4ClassRunner.java:68)
    at org.junit.runners.BlockJUnit4ClassRunner.runchild(BlockJUnit4ClassRunner.java:47)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
    at org.junit.runners.ParentRunner.runchildren(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
    at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:365)
    at org.apache.maven.surefire.junit4.JUnit4Provider.executeWithRerun(JUnit4Provider.java:273)
    at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:238)
    at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:159)
    at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:379)
    at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:340)
    at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:125)
    at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:413)
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not set com.google.api.services.logging.v2.model.LogEntryOperation field com.google.api.services.logging.v2.model.LogEntry.operation to java.util.LinkedHashMap (through reference chain: com.google.api.services.logging.v2.model.LogEntry["operation"])
    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:397)
    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:356)
    at com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase.wrapAndThrow(ContainerDeserializerBase.java:181)
    at com.fasterxml.jackson.databind.deser.std.MapDeserializer._readAndBindStringKeyMap(MapDeserializer.java:552)
    at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:377)
    at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:29)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4524)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3503)
    at com.abc.logging.data.parsing.LogEntryParser.parse(LogEntryParser.java:81)
    ... 28 more
Caused by: java.lang.IllegalArgumentException: Can not set com.google.api.services.logging.v2.model.LogEntryOperation field com.google.api.services.logging.v2.model.LogEntry.operation to java.util.LinkedHashMap
    at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
    at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
    at java.base/jdk.internal.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:81)
    at java.base/java.lang.reflect.Field.set(Field.java:780)
    at com.google.api.client.util.FieldInfo.setFieldValue(FieldInfo.java:275)
    at com.google.api.client.util.FieldInfo.setValue(FieldInfo.java:231)
    at com.google.api.client.util.GenericData.put(GenericData.java:98)
    at com.google.api.client.util.GenericData.put(GenericData.java:43)
    at com.fasterxml.jackson.databind.deser.std.MapDeserializer._readAndBindStringKeyMap(MapDeserializer.java:547)
    ... 33 more
[ERROR] Tests run: 1,Failures: 1,Errors: 0,Skipped: 0,Time elapsed: 1.583 s <<< FAILURE! - in com.abc.logging.data.parsing.LogEntryParserTest
[ERROR] testMapper(com.abc.logging.data.parsing.LogEntryParserTest)  Time elapsed: 0.904 s  <<< FAILURE!
java.lang.AssertionError
    at com.abc.logging.data.parsing.LogEntryParserTest.testMapper(LogEntryParserTest.java:212)

编辑 2:我尝试将 Mixin 与 @JsonDeserialize 和单独的反序列化类一起用于这些对象,但它似乎并没有调用反序列化类。它仍然失败并出现相同的异常。

Mixin 类:

public abstract class LogEntryOperationMixin {
    @JsonDeserialize(using = MyLogEntryOpDeserializer.class)
    private LogEntryOperation operation;
}

反序列化类:

public class MyLogEntryOpDeserializer extends JsonDeserializer<LogEntryOperation> {

    @Override
    public LogEntryOperation deserialize(JsonParser parser,DeserializationContext deserializer) {
        LogEntryOperation entryOperation = new LogEntryOperation();
        // ... populate with data
        return entryOperation;
    }
}

我的主要课程:

ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(LogEntry.class,LogEntryOperationMixin.class);
mapper.readValue(jsonData,LogEntry.class);

解决方法

所以我正在寻找错误的道路,以确定如何开箱即用的杰克逊来做到这一点(例如,没有定制)。 google-api-client 对象扩展了 GenericJson,并且与 @Key 注释一起,Jackson 无法反序列化子类并尝试将它们默认为 LinkedHashMaps。

一种方法是使用克隆或包装器并手动解析这些子类(通过 mixin 和 @JsonDeserialize 注释)。然后将这些映射回原始的 Google 对象。它并不优雅,但可以完成工作。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。