在jackson中反序列化嵌套对象

8hhllhi2  于 2021-09-13  发布在  Java
关注(0)|答案(0)|浏览(703)

这应该是直截了当的基于我所看到的文档和例子,但我还没有设法让这个工作。我很抱歉,如果这已经张贴在其他地方,但我还没有找到相同的问题,即使它涉及到与jackson库的嵌套对象反序列化。
主要项目:
jackson解析器正在尝试将嵌套对象Map到linkedhashmap,而不是实际的对象/类类型
使用第三方类,因此不可能有注解
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等。该对象主要有字符串、整数和Map等简单对象,但也有一些是上述对象(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方法并将我要Map到的类传递给它应该非常简单,如下所示:

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使用config(序列化功能和反序列化功能),但找不到任何有帮助的东西。我假设当您将类作为参数传递时,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);

暂无答案!

目前还没有任何答案,快来回答吧!

相关问题