首页 > 解决方案 > 如何使用杰克逊有条件地(在其他属性上)反序列化属性?

问题描述

我正在尝试创建一个 POJO 作为模板来反映传入的 JSON。

public class Item {
    @JsonProperty("special")
    @NotNull
    private Boolean special;

    @JsonProperty("specialCriteria")
    private SpecialCriteria specialCriteria;
 }

我想用IllegalArgumentExceptionif special 为 true 并且 JSON 中没有提供 specialCriteria 参数来使请求无效。

我尝试使用@JsonSetter以下内容,HTTP 200但当我发出一个 JSON 请求时收到了接受,其中 special 为 true 且未包含 specialCriteria。

@JsonSetter("specialCriteria")
public void setSpecialCriteria(@JsonProperty("specialCriteria") SpecialCriteria specialCriteria) {
    if(this.special == false)
        specialCriteria = null;
    if(this.special == true && specialCriteria != null)
        this.specialCriteria = specialCriteria;
    else
        throw new IllegalArgumentException("Invalid JSON. Please provide Special Criteria.");
}

我还尝试了以下方法:

public void setSpecialCriteria(@JsonProperty("specialCriteria") SpecialCriteria specialCriteria, @JsonProperty("special") Boolean special) {

我如何告诉杰克逊在创建 pojo 时设置这些限制?

跟进:如果我想在 SpecialCriteria 类中添加额外的参数限制,@Valid您给定的解决方案是否仍会坚持使用?

标签: javajackson

解决方案


您的方法提出了两个重要问题:

1)您在特定设置器中的验证方式不一致,因为您不知道杰克逊在反序列化过程(JSON 到 java 对象)期间将按哪些顺序设置字段。所以一个字段可能是null因为它还没有被初始化。所以验证可能不一致

2)Jackson 被优化为在反序列化期间使用 setter 设置字段,仅当值存在时。它解析 JSON 并反序列化发现的令牌,不多也不少。

因此,如果 JSON 中的值为空,则永远不会在 Java 对象上调用 setter。
该信息不容易发现,但就是在这里: com.fasterxml.jackson.databind.deser.BeanDeserializer:

@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
{
    // common case first
    if (p.isExpectedStartObjectToken()) {
        if (_vanillaProcessing) {
            return vanillaDeserialize(p, ctxt, p.nextToken());
        }
        // 23-Sep-2015, tatu: This is wrong at some many levels, but for now... it is
        //    what it is, including "expected behavior".
        p.nextToken();
        if (_objectIdReader != null) {
            return deserializeWithObjectId(p, ctxt);
        }
        return deserializeFromObject(p, ctxt);
    }
    return _deserializeOther(p, ctxt, p.getCurrentToken());
}

这里 p.nextToken();根据接收到的 JSON 返回下一个要反序列化的 token。

JSONParser.nextToken()定义为:

主迭代方法,它将推进流足以确定下一个令牌的类型(如果有)。如果没有剩余(流在结束前除了可能的空白之外没有其他内容),将返回 null。


长话短说,您必须使用 API 或旨在验证模型的方法,而不是尝试在 setter 中执行验证逻辑,这是不正确的方法。

一些选项:

1) Bean 验证 API。这可能会有所帮助:http ://hibernate.org/validator/documentation/getting-started/ 。
这对于字段的注释约束非常有用。

2) 对于代码中的结构化验证:根据某些规则不同的验证,您不能依赖字段上的注释约束。
因此,要么创建验证函数,要么作为替代方案,您可以在类的构造函数中包含逻辑验证。如果你用 注释构造函数,这是可能的@JsonCreator


推荐阅读