Consider a JSON representation with one string and two arrays. For example,
{
"type" : "A",
"ListA" : []
"ListB" : [3, 4, 5]
}
In the above case, type
is required field, but ListA
and ListB
are conditionally required for deserialization based on the value of type
. In other words, ListA
is only required if type
had value A
and ListB
is only required if type
had a value B
.
Currently, I am working in Jackson and in Java, and I have been able to implement making type
field mandatory by creating POJO
as following:
public class Example {
@JsonProperty(required = true)
String type;
// getter and setter auto-generated
But I can't just attach another @JsonProperty(required = true)
to ListA
or ListB
since it's dependent on the value of type
.
How can I conditionally require ListA
and ListB
for deserialization based on the value of type
?
Also, I will be performing additional checks such as whether either ListA
or ListB
is an empty array (size == 0
) or not.
You could use a custom deserializer to achieve it.
Your Example
class would be like:
public class Example {
private String type;
private List<Integer> listA;
private List<Integer> listB;
// Getters and setters omitted
}
Your custom deserializer could be as follwing:
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;
}
}
Register the custom deserializer defined above to your 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);
Use the custom serializer:
String json = "{\"type\":\"A\",\"ListA\":[1,2,3]}";
Example example = mapper.readValue(json, Example.class);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With