如何解决反序列化 Jackson 中的嵌套对象
根据我所看到的文档和示例,这应该是直接的,但我还没有设法让它工作。如果此问题已发布在其他地方,我深表歉意,但即使它与使用 Jackson 库的嵌套对象反序列化有关,我也无法找到相同的问题。
关键项目:
基本上,我的 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 举报,一经查实,本站将立刻删除。