如何使用Jackson在有条件的情况下(在其他属性上)反序列化属性?

时间:2019-01-06 18:56:27

标签: java jackson

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

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

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

如果special为true并且JSON中没有提供specialCriteria参数,我想用IllegalArgumentException使请求无效。

我尝试使用以下@JsonSetter进行操作,但在我发出JSON请求时收到了HTTP 200,其中的特殊字符为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吗?

1 个答案:

答案 0 :(得分:2)

您的方法存在两个重要问题:

1)您在特定设置器中进行验证的方式不一致,因为您不知道在反序列化过程中(JSON到Java Object),Jackson将按哪些顺序设置字段。因此,字段可能为null,因为它尚未初始化。 因此验证可能不一致

2)Jackson进行了优化,只有在反序列化过程中,只要存在该值,就可以使用setter设置字段。它解析JSON并反序列化发现的令牌(不多也不少)。

因此,如果JSON中的值为空,则setter将永远不会在Java对象上被调用。
这些信息不容易发现,但是在这里: 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反序列化。

JSONParser.nextToken()定义为:

  

主要迭代方法,该方法将使流足够前进以确定   下一个令牌的类型(如果有)。如果没有剩余(流没有   内容,而不是结束前可能的空白),则将为null   返回。


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

一些选项:

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

2)对于代码中的结构化验证:根据某些规则的验证,您不能依赖字段的注释约束。
因此,要么创建验证函数,要么可以将逻辑验证包括在类的构造函数中。如果您用@JsonCreator注释构造函数,则有可能。