将空 JSON 数组反序列化为空 TreeMap

Posted

技术标签:

【中文标题】将空 JSON 数组反序列化为空 TreeMap【英文标题】:Deserialize empty JSON array into empty TreeMap 【发布时间】:2022-01-11 01:18:53 【问题描述】:

我是 Java 新手,我想知道如何将空 JSON 数组反序列化为 TreeMap<String, MyOtherClass> 类型的空 Java 对象。

目前,我正在尝试反序列化一个包含对象数组的 JSON 文件,并将每个对象反序列化为一个名为 MyClass 的类。类大致如下:

@JsonIgnoreProperties(ignoreUnknown = true)
public class MyClass 

  private final String propertyOne;
  private final String propertyTwo;
  private final String propertyThree;

  @JsonSerialize(contentAs = MyOtherClass.class)
  @JsonDeserialize(contentAs = MyOtherClass.class)
  TreeMap<String, MyOtherClass> otherThings = new TreeMap<>();

  @JsonCreator
  public MyClass(
      @JsonProperty("propertyOne") String propertyOne,
      @JsonProperty("propertyTwo") String propertyTwo,
      @JsonProperty("propertyThree") String propertyThree) 
    this.propertyOne = propertyOne;
    this.propertyTwo = propertyTwo;
    this.propertyThree = propertyThree;

  // Getters and setters below
  @JsonSetter("otherThings")
  public void setOtherThings() 
    if (this.otherThings == null || this.otherThings.isEmpty()) 
      this.otherThings = new TreeMap<>();
    
  
  

原始 JSON 中的一个条目是该字段 otherThings。我使用MyOtherClass 表示了这个条目,它处理otherThings 可能包含的属性。

目前,这非常适合序列化,以及填充otherThings 时的反序列化。但是,当我遇到以下情况时:


  "propertyOne": "This is propertyOne!",
  "propertyTwo": "This is propertyTwo!",
  "propertyThree": "This is propertyThree!",
  "otherThings": []

我得到以下堆栈跟踪:

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.util.TreeMap<java.lang.String,org.something.model.MyOtherClass>` from Array value (token `JsonToken.START_ARRAY`)
 at [Source: (URL); line: 7, column: 14] (through reference chain: java.util.ArrayList[0]->org.something.model.Order["lines"])
    at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)
    at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1741)
    at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1515)
    at com.fasterxml.jackson.databind.deser.std.StdDeserializer._deserializeFromArray(StdDeserializer.java:222)
    at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:447)
    at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:32)
    at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:138)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:277)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:462)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1405)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:351)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:184)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:355)
    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.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4675)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3572)
    at org.something.model.MyClass.populateMyClassFromJson(MyClass.java:148)
    at org.something.service.MyClassService.displayMyClassObject(MyClassService.java:96)
    at Main.main(Main.java:9)

我试过了:

使用ObjectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT),但这并没有真正的帮助,因为otherThings 被实例化为null。 将JsonSetter 用于上面看到的setOtherThings。这必须小心,因为如果otherThings 已经包含其中的内容,我不想覆盖它。

编辑:如果有帮助,这也是方法populateMyClassFromJson

public void populateMyClassFromJson() throws IOException 
  List<MyClass> myClassList = mapper.readValue(new File("/path/to/my_classes.json"), new TypeReference<>() );
  Map<String, MyClass> myClassMap = myClassList.stream().collect(Collectors.toMap(MyClass::getId, Function.identity()));
  myClassTreeMap.putAll(myClassMap);

【问题讨论】:

您可以将此地图作为参数添加到您的构造函数中,并检查地图是否为空。这适用于ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT。并且不需要JsonSetter 感谢您的建议。我想看看是否有办法在杰克逊本地做到这一点。我可以这样做,但如果我不需要,我不喜欢将null 显式传递给构造函数。 MyClass 和 MyOtherClass 现在是不是有些混淆了? 【参考方案1】:

只是总结一下(我相信从一开始就很清楚):正如错误所述,问题是杰克逊试图将字段反序列化为 TreeMap,这将不起作用,因为在 JSON 中有一个数组 []

但是你的二传手对我来说有点奇怪。我相信你应该有这样的东西(根据你的评论和更新进行更新):

@JsonSetter("otherThings")
public void setOtherThings(MyOtherClass[] arr) 
    this.otherThings = List.of(Optional.ofNullable(arr)
            .orElse(new MyOtherClass[0])).stream()
        .collect(Collectors.toMap(MyOtherClass::getId, Function.identity(),
                (o1, o2) -> o1, TreeMap::new));

当然你也应该处理数组不为空的情况。

【讨论】:

感谢您的建议。我在上面添加了方法populateMyClassFromJson。我使用它来填充包含所有MyClass 实例的TreeMap,以及唯一标识它们的ID。我使用Map 来保存MyClass 实例,我将它们放在TreeMap 中。所以结构是:TreeMap&lt;String, MyClass&gt; myClassTreeMap,它本身包含MyClass 实例,其中包含otherThings,即TreeMap&lt;String, MyOtherClass&gt;。这是嵌套的深度,仅此而已。我可以改用Object[] 吗?能详细说明一下吗? @ScottCrooks 还要准确地添加您尝试反序列化的 JSON。假设您使用非空的 MyOtherClass 列表/数组反序列化 MyClass。 @ScottCrooks 有点基于猜测的更新 :)

以上是关于将空 JSON 数组反序列化为空 TreeMap的主要内容,如果未能解决你的问题,请参考以下文章

使用 XmlSerializer 将空 xml 属性值反序列化为可为空的 int 属性

如何反序列化json空数组

将空 XML 元素反序列化为 Guid.Empty

无法将 appsettings IConfiguration 部分反序列化为类型化元素的数组

无法将 JSON 数组反序列化为类型 - Json.NET

在 C# 中将 JSON 数组反序列化为对象