基于 Jackson 中另一个字段值的条件字段要求?

Conditional field requirement based on another field value in Jackson?

考虑一个 JSON 表示,其中包含一个字符串和两个数组。例如,

{
    "type" : "A",
    "ListA" : []
    "ListB" : [3, 4, 5]
 }

在上面的例子中,type是必填字段,但是ListAListB有条件根据值反序列化所必需的type 个。换句话说,仅当 type 具有值 A 时才需要 ListA,而仅当 type 具有值 B 时才需要 ListB。 =34=]

目前,我在 Jackson 和 Java 工作,我已经能够通过如下创建 POJO 来实现强制 type 字段:

public class Example {
    @JsonProperty(required = true)
    String type;

    // getter and setter auto-generated

但我不能只将另一个 @JsonProperty(required = true) 附加到 ListAListB,因为它取决于 type.

的值

如何根据 type 的值有条件地要求 ListAListB 进行反序列化?

此外,我将执行额外的检查,例如 ListAListB 是否为空数组 (size == 0)。

使用 Jackson,您可以为示例 POJO 创建自己的自定义反序列化器,扩展 StdDeserializer class 并覆盖 deserialize() 方法与你的逻辑。在这里您可以检查类型和列表大小。

然后,为了使用您的自定义反序列化器,您必须将其添加到 SimpleModule 并向您的 Jackson ObjectMapper 注册后者

我之前写过几篇关于这个主题的文章,您可以在其中找到关于自定义 Serialization/Deserialization 和 Jackson 的具体示例:

Jackson: create and register a custom JSON serializer with StdSerializer and SimpleModule classes

Jackson: create a custom JSON deserializer with StdDeserializer and JsonToken classes

您可以使用自定义解串器来实现它。

定义模型

您的 Example class 会像:

public class Example {

    private String type;
    private List<Integer> listA;
    private List<Integer> listB;

    // Getters and setters omitted    
}

创建自定义解串器

您的自定义解串器可能如下所示:

public class ExampleDeserializer extends StdDeserializer<Example> {

    private static final String TYPE_A = "A";
    private static final String TYPE_B = "B";

    public ExampleDeserializer() {
        super(Example.class);
    }

    @Override
    public Example deserialize(JsonParser p, DeserializationContext ctxt) 
                   throws IOException, JsonProcessingException {

        ObjectMapper mapper = (ObjectMapper) p.getCodec();  
        JsonNode tree = mapper.readTree(p);  

        Example example = new Example();

        JsonNode typeNode = tree.get("type");
        if (typeNode == null || typeNode.asText().isEmpty()) {
            throw ctxt.mappingException("\"type\" is required");
        }
        example.setType(typeNode.asText());

        switch (typeNode.asText()) {

        case TYPE_A:
            ArrayNode listANode = (ArrayNode) tree.get("ListA");
            if (listANode == null || listANode.size() == 0) {
                throw ctxt.mappingException(
                           "\"ListA\" is required when \"type\" is \"" + TYPE_A + "\"");
            }
            example.setListA(createList(listANode));
            break;

        case TYPE_B:
            ArrayNode listBNode = (ArrayNode) tree.get("ListB");
            if (listBNode == null || listBNode.size() == 0) {
                throw ctxt.mappingException(
                           "\"ListB\" is required when \"type\" is \"" + TYPE_B + "\"");
            }
            example.setListB(createList(listBNode));
            break;

        default:
            throw ctxt.mappingException(
                       "\"type\" must be \"" + TYPE_A + "\" or \"" + TYPE_B + "\"");
        }


        return example;
    }

    private List<Integer> createList(ArrayNode arrayNode) {
        List<Integer> list = new ArrayList<Integer>();
        for (JsonNode node : arrayNode) {
            list.add(node.asInt());
        }
        return list;
    }
}

正在注册自定义解串器

将上面定义的自定义反序列化器注册到您的 ObjectMapper:

SimpleModule module = new SimpleModule("ExampleDeserializer", 
        new Version(1, 0, 0, null, "com.example", "example-deserializer")); 

ExampleDeserializer exampleDeserializer = new ExampleDeserializer();
module.addDeserializer(Example.class, exampleDeserializer);

ObjectMapper mapper = new ObjectMapper()
                          .registerModule(module)
                          .enable(SerializationFeature.INDENT_OUTPUT);

正在测试您的自定义解串器

使用自定义序列化程序:

String json = "{\"type\":\"A\",\"ListA\":[1,2,3]}";
Example example = mapper.readValue(json, Example.class);