英文:
Jackson deserialize array of mixed types using @JsonTypeInfo with extra fields
问题
这是您的翻译内容:
我有一个包含混合类型数组的 JSON 文件,其结构如下:
{
"operations": [
{
"extension": {
"serviceId": "id",
"serviceType": "type"
},
"name": "name",
"tags": "tags"
},
{
"core": {
"config": {
"a": 90,
"b": 45
},
"displayName": "displayName"
},
"name": "name",
"tags": "tags"
},
{
"extension": {
"serviceId": "abc",
"serviceType": "xyz"
},
"name": "name",
"tags": "tags"
}
]
}
根据这个 [Stackoverflow][1] 主题,我创建了用于混合类型 JSON 映射的 Java 模型。
我的 Java 模型如下:
@Data
public class Payload {
private List<OperationElement> operations;
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
@JsonSubTypes({ @JsonSubTypes.Type(value = Extension.class, name = "extension"),
@JsonSubTypes.Type(value = Core.class, name = "core") })
public static abstract class OperationElement {
private String name;
private String tags;
}
@Data
@JsonRootName("extension")
public static class Extension extends OperationElement {
private String serviceType;
private String serviceId;
}
@Data
@JsonRootName("core")
public static class Core extends OperationElement {
private Config config;
private String displayName;
@Data
public static class Config {
private Long a;
private Long b;
}
}
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
Payload operationsPayload = mapper.readValue(
Files.readAllBytes(Paths.get("C:\\desc", "file.json")), Payload.class);
System.out.println(operationsPayload);
}
}
当我尝试将 JSON 解析为 Java 模型时,出现了以下错误:
Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (FIELD_NAME), expected END_OBJECT: expected closing END_OBJECT after type information and deserialized value
line: 8, column: 7] (through reference chain: com.pojo.Payload["operations"]->java.util.ArrayList[0])
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)
at com.fasterxml.jackson.databind.DeserializationContext.wrongTokenException(DeserializationContext.java:1799)
at com.fasterxml.jackson.databind.DeserializationContext.reportWrongTokenException(DeserializationContext.java:1533)
at com.fasterxml.jackson.databind.jsontype.impl.AsWrapperTypeDeserializer._deserialize(AsWrapperTypeDeserializer.java:124)
at com.fasterxml.jackson.databind.jsontype.impl.AsWrapperTypeDeserializer.deserializeTypedFromObject(AsWrapperTypeDeserializer.java:52)
at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserializeWithType(AbstractDeserializer.java:263)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:349)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:244)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:28)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:324)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:187)
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4593)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3609)
如果我移除
"name": "name",
"tags": "tags"
那么代码可以运行。
但我必须在 JSON 结构中同时包含这两个键值对。
请建议我有什么解决方案。提前感谢。
[1]: https://stackoverflow.com/questions/38875940/how-to-deserialize-a-json-array-containing-different-types-of-objects
请注意,我已经将 HTML 编码的引号 ("
) 替换为正常的双引号 ("
),以使代码和文本更容易理解。
英文:
I have a json file which contains an array of mixed types. Its structure look like this:
{
"operations": [
{
"extension": {
"serviceId": "id",
"serviceType": "type"
},
"name": "name",
"tags": "tags"
},
{
"core": {
"config": {
"a": 90,
"b": 45
},
"displayName": "displayName"
},
"name": "name",
"tags": "tags"
},
{
"extension": {
"serviceId": "abc",
"serviceType": "xyz"
},
"name": "name",
"tags": "tags"
}
]
}
Follow this topic Stackoverflow I create my model for json mapping of mixed types.
My java model:
@Data
public class Payload {
private List<OperationElement> operations;
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
@JsonSubTypes({ @JsonSubTypes.Type(value = Extension.class, name = "extension"),
@JsonSubTypes.Type(value = Core.class, name = "core") })
public static abstract class OperationElement {
private String name;
private String tags;
}
@Data
@JsonRootName("extension")
public static class Extension extends OperationElement {
private String serviceType;
private String serviceId;
}
@Data
@JsonRootName("core")
public static class Core extends OperationElement {
private Config config;
private String displayName;
@Data
public static class Config {
private Long a;
private Long b;
}
}
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
Payload operationsPayload = mapper.readValue(
Files.readAllBytes(Paths.get("C:\\desc", "file.json")), Payload.class);
System.out.println(operationsPayload );
}
}
I got this error when trying to parse the json to java model:
Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (FIELD_NAME), expected END_OBJECT: expected closing END_OBJECT after type information and deserialized value
line: 8, column: 7] (through reference chain: com.pojo.Payload["operations"]->java.util.ArrayList[0])
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)
at com.fasterxml.jackson.databind.DeserializationContext.wrongTokenException(DeserializationContext.java:1799)
at com.fasterxml.jackson.databind.DeserializationContext.reportWrongTokenException(DeserializationContext.java:1533)
at com.fasterxml.jackson.databind.jsontype.impl.AsWrapperTypeDeserializer._deserialize(AsWrapperTypeDeserializer.java:124)
at com.fasterxml.jackson.databind.jsontype.impl.AsWrapperTypeDeserializer.deserializeTypedFromObject(AsWrapperTypeDeserializer.java:52)
at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserializeWithType(AbstractDeserializer.java:263)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:349)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:244)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:28)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:324)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:187)
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4593)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3609)
If I remove
"name": "name",
"tags": "tags"
The code can run.
But I must have both these key-value in the json structure.
Any solution please suggest me. Thanks in advance.
答案1
得分: 2
JsonTypeInfo.As#WRAPPER_OBJECT
仅适用于序列化过程,当您尝试反序列化 JSON 输入时,可以使用 JsonTypeInfo.Id#DEDUCTION
来推断正确的类型从现有属性中。以您的 JSON 输入的简化版本为例,如下:
{
"operations": [
{
"extension": {
"serviceId": "id",
"serviceType": "type"
},
"name": "name",
"tags": "tags"
}
]
}
您有一个包含三个属性 extension, name, tag 的匿名对象数组,可以像下面这样反序列化:
@Data
public class Payload {
List<OperationWrapper> operations;
}
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION)
@JsonSubTypes({
@JsonSubTypes.Type(value = ExtensionWrapper.class)
})
public abstract class OperationWrapper {}
@Data
public class ExtensionWrapper extends OperationWrapper {
private Extension extension;
private String name;
private String tags;
}
@Data
public class Extension {
private String serviceType;
private String serviceId;
}
Payload payload = mapper.readValue(json, Payload.class);
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(payload));
输出:
{
"operations": [
{
"extension": {
"serviceType": "type",
"serviceId": "id"
},
"name": "name",
"tags": "tags"
}
]
}
同样的机制可以应用于您希望进行反序列化的其他类。
英文:
The JsonTypeInfo.As#WRAPPER_OBJECT
works only for the serialization process while you are trying to deserialize your json input. In this case you can use the JsonTypeInfo.Id#DEDUCTION
to deduce the correct type from the existing properties. Taking for example a simplified version of your json input like below:
{
"operations": [
{
"extension": {
"serviceId": "id",
"serviceType": "type"
},
"name": "name",
"tags": "tags"
}
]
}
You have an array containing an anonymous object with the three properties extension, name, tag that can be deserialized like below:
@Data
public class Payload {
List<OperationWrapper> operations;
}
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION)
@JsonSubTypes({
@JsonSubTypes.Type(value = ExtensionWrapper.class)
})
public abstract class OperationWrapper {}
@Data
public class ExtensionWrapper extends OperationWrapper {
private Extension extension;
private String name;
private String tags;
}
@Data
public class Extension {
private String serviceType;
private String serviceId;
}
Payload payload = mapper.readValue(json, Payload.class);
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(payload));
Output:
{
"operations" : [ {
"extension" : {
"serviceType" : "type",
"serviceId" : "id"
},
"name" : "name",
"tags" : "tags"
} ]
}
The same mechanism can be applied to other classes you want to be deserialized.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论