基于 Jackson 中另一个字段值的条件字段要求?
Conditional field requirement based on another field value in Jackson?
考虑一个 JSON 表示,其中包含一个字符串和两个数组。例如,
{
"type" : "A",
"ListA" : []
"ListB" : [3, 4, 5]
}
在上面的例子中,type
是必填字段,但是ListA
和ListB
是有条件根据值反序列化所必需的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)
附加到 ListA
或 ListB
,因为它取决于 type
.
的值
如何根据 type
的值有条件地要求 ListA
和 ListB
进行反序列化?
此外,我将执行额外的检查,例如 ListA
或 ListB
是否为空数组 (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);
考虑一个 JSON 表示,其中包含一个字符串和两个数组。例如,
{
"type" : "A",
"ListA" : []
"ListB" : [3, 4, 5]
}
在上面的例子中,type
是必填字段,但是ListA
和ListB
是有条件根据值反序列化所必需的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)
附加到 ListA
或 ListB
,因为它取决于 type
.
如何根据 type
的值有条件地要求 ListA
和 ListB
进行反序列化?
此外,我将执行额外的检查,例如 ListA
或 ListB
是否为空数组 (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);