如何忽略在Jackson中引起反序列化错误的列表条目?

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

How to ignore a list entry causing a deserialization error in Jackson?

问题

我正试图使用Jackson对JSON结构进行反序列化,我正在使用一个类似这样的DTO:

public class RootLevelDTO {
   
    private List<ComplexEntry> complexEntries;
    // ... 其他字段,与问题无关

}

现在,ComplexEntry 可能有子类型,这些子类型具有枚举类型的属性等。如果通信的另一侧更新了他们的API,例如添加了另一个子类型或添加了枚举字面量,很多问题可能会出现。

我想做的是告诉Jackson:

  • 如果在反序列化 complexEntries 字段时遇到任何数据绑定错误...
  • ... 不要抛出异常,而是忽略这个条目并继续处理下一个。

到目前为止,我尝试过为 ComplexEntry 使用一个委托反序列化器:

public class ComplexEntryDeserializer extends StdDeserializer<ComplexEntry> {

    private StdDeserializer<ComplexEntry> delegate;

    public ComplexEntryDeserializer(StdDeserializer<ComplexEntry> delegate){
        this.delegate = delegate;
    }

    public ComplexEntry deserialize(JsonParser p, DeserializationContext ctxt){
        try {
            return this.delegate.deserialize(p, ctxt);
        }catch(Exception e){
            // 列表条目无法反序列化,但是我们必须在这里返回*一些内容*
            return null;
        }
    }
    
    // ... 其他必需的方法,与此无关
}

这个解决方案的问题是它会向 complexEntries 列表引入 null 值,然后我必须使用 Converter 显式地摆脱它们。

有没有更优雅的解决方案来解决这个问题?

英文:

I'm trying to deserialize a JSON structure with Jackson and I'm working with a DTO that looks like this:

public class RootLevelDTO {
   
    private List&lt;ComplexEntry&gt; complexEntries;
    // ... other fields, not relevant

}

Now, the ComplexEntry can have sub-types, those have properties of enum types etc. A lot can go wrong here if the other side of the communication updates their API and e.g. adds another sub type or adds an enum literal.

What I would like to do is to tell Jackson:

  • if you encounter any databinding error during deserialization of the complexEntries field...
  • ... do not throw an exception, but instead ignore this entry and continue with the next.

What I tried so far is to use a delegating deserializer for ComplexEntry:

public class ComplexEntryDeserializer extends StdDeserializer&lt;ComplexEntry&gt; {

    private StdDeserializer&lt;ComplexEntry&gt; delegate;

    public ComplexEntryDeserializer(StdDeserializer&lt;ComplexEntry&gt; delegate){
        this.delegate = delegate;
    }

    public ComplexEntry deserialize(JsonParser p, DeserializationContext ctxt){
        try {
            return this.delegate.deserialize(p, ctxt);
        }catch(Exception e){
            // the list entry failed to deserialize, but we have to return *something* here
            return null;
        }
    }
    
    // ... other mandatory methods, not relevant here
}

This solution has the problem that it will introduce null values to the complexEntries list, which I then have to explicitly get rid of with a Converter.

Is there a more elegant solution to this problem?

答案1

得分: 2

以下是翻译好的部分:

"After a lot of tinkering I've ended up with the following solution. It doesn't require any additional jackson modules or other magic, only a single (specific) deserializer.

DTO(数据传输对象):

public class RootLevelDTO {

    // use a custom deserializer for the list
    @JsonDeserialize(using = ListOfComplexEntryDeserializer.class)
    private List&lt;ComplexEntry&gt; complexEntries;

}

Deserializer(反序列化器):

public class ListOfComplexEntryDeserializer extends JsonDeserializer&lt;List&lt;ComplexEntry&gt;&gt; {

    @Override
    public List&lt;ComplexEntry&gt; deserialize(JsonParser p, DeserializationContext ctxt) {
        List&lt;ComplexEntry&gt; resultList = new ArrayList&lt;&gt;();
        while(p.nextToken() != JsonToken.END_ARRAY){
            try {
                // delegate the deserialization of the individual list entries to the standard deserializers
                resultList.add(ctxt.readValue(p, ComplexEntry.class))
            }catch(Exception e){
                // log that the entry wasn't deserialized properly
                System.out.println(&quot;ComplexEntry could not be read and will be ignored.&quot;);
            }
        }
        return resultList;
    }

}

Big disclaimer: While the code above works, it's not something you should go for by design. I'm really with my back to the wall here and have no other choice (due to external factors beyond my control), and for that case it works."

英文:

After a lot of tinkering I've ended up with the following solution. It doesn't require any additional jackson modules or other magic, only a single (specific) deserializer.

DTO:

public class RootLevelDTO {

    // use a custom deserializer for the list
    @JsonDeserialize(using = ListOfComplexEntryDeserializer.class)
    private List&lt;ComplexEntry&gt; complexEntries;

}

Deserializer:

public class ListOfComplexEntryDeserializer extends JsonDeserializer&lt;List&lt;ComplexEntry&gt;&gt; {

    @Override
    public List&lt;ComplexEntry&gt; deserialize(JsonParser p, DeserializationContext ctxt) {
        List&lt;ComplexEntry&gt; resultList = new ArrayList&lt;&gt;();
        while(p.nextToken() != JsonToken.END_ARRAY){
            try {
                // delegate the deserialization of the individual list entries to the standard deserializers
                resultList.add(ctxt.readValue(p, ComplexEntry.class))
            }catch(Exception e){
                // log that the entry wasn&#39;t deserialized properly
                System.out.println(&quot;ComplexEntry could not be read and will be ignored.&quot;);
            }
        }
        return resultList;
    }

}

Big disclaimer: While the code above works, it's not something you should go for by design. I'm really with my back to the wall here and have no other choice (due to external factors beyond my control), and for that case it works.

huangapple
  • 本文由 发表于 2020年8月24日 18:48:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/63559544.html
匿名

发表评论

匿名网友

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

确定