Jackson使用@JsonTypeInfo反序列化包含额外字段的混合类型数组

huangapple go评论67阅读模式
英文:

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 编码的引号 (&quot;) 替换为正常的双引号 ("),以使代码和文本更容易理解。

英文:

I have a json file which contains an array of mixed types. Its structure look like this:

{
&quot;operations&quot;: [
{
&quot;extension&quot;: {
&quot;serviceId&quot;: &quot;id&quot;,
&quot;serviceType&quot;: &quot;type&quot;
},
&quot;name&quot;: &quot;name&quot;,
&quot;tags&quot;: &quot;tags&quot;
},
{
&quot;core&quot;: {
&quot;config&quot;: {
&quot;a&quot;: 90,
&quot;b&quot;: 45
},
&quot;displayName&quot;: &quot;displayName&quot;
},
&quot;name&quot;: &quot;name&quot;,
&quot;tags&quot;: &quot;tags&quot;
},
{
&quot;extension&quot;: {
&quot;serviceId&quot;: &quot;abc&quot;,
&quot;serviceType&quot;: &quot;xyz&quot;
},
&quot;name&quot;: &quot;name&quot;,
&quot;tags&quot;: &quot;tags&quot;
}
]
}

Follow this topic Stackoverflow I create my model for json mapping of mixed types.

My java model:

@Data
public class Payload {
private List&lt;OperationElement&gt; operations;
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
@JsonSubTypes({ @JsonSubTypes.Type(value = Extension.class, name = &quot;extension&quot;),
@JsonSubTypes.Type(value = Core.class, name = &quot;core&quot;) })
public static abstract class OperationElement {
private String name;
private String tags;
}
@Data
@JsonRootName(&quot;extension&quot;)
public static class Extension extends OperationElement {
private String serviceType;
private String serviceId;
}
@Data
@JsonRootName(&quot;core&quot;)
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(&quot;C:\\desc&quot;, &quot;file.json&quot;)), Payload.class);
System.out.println(operationsPayload );
}
}

I got this error when trying to parse the json to java model:

Exception in thread &quot;main&quot; 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[&quot;operations&quot;]-&gt;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

&quot;name&quot;: &quot;name&quot;,
&quot;tags&quot;: &quot;tags&quot;

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:

{
&quot;operations&quot;: [
{
&quot;extension&quot;: {
&quot;serviceId&quot;: &quot;id&quot;,
&quot;serviceType&quot;: &quot;type&quot;
},
&quot;name&quot;: &quot;name&quot;,
&quot;tags&quot;: &quot;tags&quot;
}
]
}

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&lt;OperationWrapper&gt; 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:

{
&quot;operations&quot; : [ {
&quot;extension&quot; : {
&quot;serviceType&quot; : &quot;type&quot;,
&quot;serviceId&quot; : &quot;id&quot;
},
&quot;name&quot; : &quot;name&quot;,
&quot;tags&quot; : &quot;tags&quot;
} ]
}

The same mechanism can be applied to other classes you want to be deserialized.

huangapple
  • 本文由 发表于 2023年2月24日 13:22:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/75552870.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定