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

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

Jackson deserialize array of mixed types using @JsonTypeInfo with extra fields

问题

这是您的翻译内容:

  1. 我有一个包含混合类型数组的 JSON 文件其结构如下
  2. {
  3. "operations": [
  4. {
  5. "extension": {
  6. "serviceId": "id",
  7. "serviceType": "type"
  8. },
  9. "name": "name",
  10. "tags": "tags"
  11. },
  12. {
  13. "core": {
  14. "config": {
  15. "a": 90,
  16. "b": 45
  17. },
  18. "displayName": "displayName"
  19. },
  20. "name": "name",
  21. "tags": "tags"
  22. },
  23. {
  24. "extension": {
  25. "serviceId": "abc",
  26. "serviceType": "xyz"
  27. },
  28. "name": "name",
  29. "tags": "tags"
  30. }
  31. ]
  32. }
  33. 根据这个 [Stackoverflow][1] 主题我创建了用于混合类型 JSON 映射的 Java 模型
  34. 我的 Java 模型如下
  35. @Data
  36. public class Payload {
  37. private List<OperationElement> operations;
  38. @Data
  39. @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
  40. @JsonSubTypes({ @JsonSubTypes.Type(value = Extension.class, name = "extension"),
  41. @JsonSubTypes.Type(value = Core.class, name = "core") })
  42. public static abstract class OperationElement {
  43. private String name;
  44. private String tags;
  45. }
  46. @Data
  47. @JsonRootName("extension")
  48. public static class Extension extends OperationElement {
  49. private String serviceType;
  50. private String serviceId;
  51. }
  52. @Data
  53. @JsonRootName("core")
  54. public static class Core extends OperationElement {
  55. private Config config;
  56. private String displayName;
  57. @Data
  58. public static class Config {
  59. private Long a;
  60. private Long b;
  61. }
  62. }
  63. public static void main(String[] args) throws Exception {
  64. ObjectMapper mapper = new ObjectMapper();
  65. Payload operationsPayload = mapper.readValue(
  66. Files.readAllBytes(Paths.get("C:\\desc", "file.json")), Payload.class);
  67. System.out.println(operationsPayload);
  68. }
  69. }
  70. 当我尝试将 JSON 解析为 Java 模型时出现了以下错误
  71. 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
  72. line: 8, column: 7] (through reference chain: com.pojo.Payload["operations"]->java.util.ArrayList[0])
  73. at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)
  74. at com.fasterxml.jackson.databind.DeserializationContext.wrongTokenException(DeserializationContext.java:1799)
  75. at com.fasterxml.jackson.databind.DeserializationContext.reportWrongTokenException(DeserializationContext.java:1533)
  76. at com.fasterxml.jackson.databind.jsontype.impl.AsWrapperTypeDeserializer._deserialize(AsWrapperTypeDeserializer.java:124)
  77. at com.fasterxml.jackson.databind.jsontype.impl.AsWrapperTypeDeserializer.deserializeTypedFromObject(AsWrapperTypeDeserializer.java:52)
  78. at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserializeWithType(AbstractDeserializer.java:263)
  79. at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:349)
  80. at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:244)
  81. at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:28)
  82. at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
  83. at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:324)
  84. at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:187)
  85. at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322)
  86. at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4593)
  87. at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3609)
  88. 如果我移除
  89. "name": "name",
  90. "tags": "tags"
  91. 那么代码可以运行
  92. 但我必须在 JSON 结构中同时包含这两个键值对
  93. 请建议我有什么解决方案提前感谢
  94. [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:

  1. {
  2. &quot;operations&quot;: [
  3. {
  4. &quot;extension&quot;: {
  5. &quot;serviceId&quot;: &quot;id&quot;,
  6. &quot;serviceType&quot;: &quot;type&quot;
  7. },
  8. &quot;name&quot;: &quot;name&quot;,
  9. &quot;tags&quot;: &quot;tags&quot;
  10. },
  11. {
  12. &quot;core&quot;: {
  13. &quot;config&quot;: {
  14. &quot;a&quot;: 90,
  15. &quot;b&quot;: 45
  16. },
  17. &quot;displayName&quot;: &quot;displayName&quot;
  18. },
  19. &quot;name&quot;: &quot;name&quot;,
  20. &quot;tags&quot;: &quot;tags&quot;
  21. },
  22. {
  23. &quot;extension&quot;: {
  24. &quot;serviceId&quot;: &quot;abc&quot;,
  25. &quot;serviceType&quot;: &quot;xyz&quot;
  26. },
  27. &quot;name&quot;: &quot;name&quot;,
  28. &quot;tags&quot;: &quot;tags&quot;
  29. }
  30. ]
  31. }

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

My java model:

  1. @Data
  2. public class Payload {
  3. private List&lt;OperationElement&gt; operations;
  4. @Data
  5. @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
  6. @JsonSubTypes({ @JsonSubTypes.Type(value = Extension.class, name = &quot;extension&quot;),
  7. @JsonSubTypes.Type(value = Core.class, name = &quot;core&quot;) })
  8. public static abstract class OperationElement {
  9. private String name;
  10. private String tags;
  11. }
  12. @Data
  13. @JsonRootName(&quot;extension&quot;)
  14. public static class Extension extends OperationElement {
  15. private String serviceType;
  16. private String serviceId;
  17. }
  18. @Data
  19. @JsonRootName(&quot;core&quot;)
  20. public static class Core extends OperationElement {
  21. private Config config;
  22. private String displayName;
  23. @Data
  24. public static class Config {
  25. private Long a;
  26. private Long b;
  27. }
  28. }
  29. public static void main(String[] args) throws Exception {
  30. ObjectMapper mapper = new ObjectMapper();
  31. Payload operationsPayload = mapper.readValue(
  32. Files.readAllBytes(Paths.get(&quot;C:\\desc&quot;, &quot;file.json&quot;)), Payload.class);
  33. System.out.println(operationsPayload );
  34. }
  35. }

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

  1. 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
  2. line: 8, column: 7] (through reference chain: com.pojo.Payload[&quot;operations&quot;]-&gt;java.util.ArrayList[0])
  3. at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)
  4. at com.fasterxml.jackson.databind.DeserializationContext.wrongTokenException(DeserializationContext.java:1799)
  5. at com.fasterxml.jackson.databind.DeserializationContext.reportWrongTokenException(DeserializationContext.java:1533)
  6. at com.fasterxml.jackson.databind.jsontype.impl.AsWrapperTypeDeserializer._deserialize(AsWrapperTypeDeserializer.java:124)
  7. at com.fasterxml.jackson.databind.jsontype.impl.AsWrapperTypeDeserializer.deserializeTypedFromObject(AsWrapperTypeDeserializer.java:52)
  8. at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserializeWithType(AbstractDeserializer.java:263)
  9. at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:349)
  10. at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:244)
  11. at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:28)
  12. at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
  13. at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:324)
  14. at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:187)
  15. at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322)
  16. at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4593)
  17. at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3609)

If I remove

  1. &quot;name&quot;: &quot;name&quot;,
  2. &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 输入的简化版本为例,如下:

  1. {
  2. "operations": [
  3. {
  4. "extension": {
  5. "serviceId": "id",
  6. "serviceType": "type"
  7. },
  8. "name": "name",
  9. "tags": "tags"
  10. }
  11. ]
  12. }

您有一个包含三个属性 extension, name, tag 的匿名对象数组,可以像下面这样反序列化:

  1. @Data
  2. public class Payload {
  3. List<OperationWrapper> operations;
  4. }
  5. @Data
  6. @JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION)
  7. @JsonSubTypes({
  8. @JsonSubTypes.Type(value = ExtensionWrapper.class)
  9. })
  10. public abstract class OperationWrapper {}
  11. @Data
  12. public class ExtensionWrapper extends OperationWrapper {
  13. private Extension extension;
  14. private String name;
  15. private String tags;
  16. }
  17. @Data
  18. public class Extension {
  19. private String serviceType;
  20. private String serviceId;
  21. }
  22. Payload payload = mapper.readValue(json, Payload.class);
  23. System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(payload));

输出:

  1. {
  2. "operations": [
  3. {
  4. "extension": {
  5. "serviceType": "type",
  6. "serviceId": "id"
  7. },
  8. "name": "name",
  9. "tags": "tags"
  10. }
  11. ]
  12. }

同样的机制可以应用于您希望进行反序列化的其他类。

英文:

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:

  1. {
  2. &quot;operations&quot;: [
  3. {
  4. &quot;extension&quot;: {
  5. &quot;serviceId&quot;: &quot;id&quot;,
  6. &quot;serviceType&quot;: &quot;type&quot;
  7. },
  8. &quot;name&quot;: &quot;name&quot;,
  9. &quot;tags&quot;: &quot;tags&quot;
  10. }
  11. ]
  12. }

You have an array containing an anonymous object with the three properties extension, name, tag that can be deserialized like below:

  1. @Data
  2. public class Payload {
  3. List&lt;OperationWrapper&gt; operations;
  4. }
  5. @Data
  6. @JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION)
  7. @JsonSubTypes({
  8. @JsonSubTypes.Type(value = ExtensionWrapper.class)
  9. })
  10. public abstract class OperationWrapper {}
  11. @Data
  12. public class ExtensionWrapper extends OperationWrapper {
  13. private Extension extension;
  14. private String name;
  15. private String tags;
  16. }
  17. @Data
  18. public class Extension {
  19. private String serviceType;
  20. private String serviceId;
  21. }
  22. Payload payload = mapper.readValue(json, Payload.class);
  23. System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(payload));

Output:

  1. {
  2. &quot;operations&quot; : [ {
  3. &quot;extension&quot; : {
  4. &quot;serviceType&quot; : &quot;type&quot;,
  5. &quot;serviceId&quot; : &quot;id&quot;
  6. },
  7. &quot;name&quot; : &quot;name&quot;,
  8. &quot;tags&quot; : &quot;tags&quot;
  9. } ]
  10. }

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:

确定