We have a scenario where we have JSON
s with 2 different values for a field. We would like to parse all the jsons using the same POJO
. Below you can find these 2 JSON
payloads:
{
"values": [
[
{
"name": "item_name",
"value": "pool"
}
],
[
{
"name": "item_name",
"value": "Mob"
}
]
],
"name": "lines"
}
And:
{
"values": [
{
"name": "pack",
"value": "Enter, HD"
}
],
"name": "lines"
}
Currently, if I specify POJO as below, 2nd json throws exception
class ValuesModel extends Serializable {
@BeanProperty
var values: List[List[ValueModel]] = _
}
if I specify POJO as below, 1st json throws exception
class ValuesModel extends Serializable {
@BeanProperty
var values: List[ValueModel] = _
}
Is there a way to create one POJO
to parse both jsons together rather than catching exception and parsing with another schema? I am using Jackson
to parse.
In cases like this where you want to handle more than one JSON
schema and be able to deserialize it to the same POJO
model you need to implement custom deserialiser and implement all required scenarios.
Below you can find example in Java
how to deserialise both JSON
payloads:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.node.MissingNode;
import com.fasterxml.jackson.databind.type.SimpleType;
import com.fasterxml.jackson.databind.util.TokenBuffer;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
public class JsonApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./src/main/resources/test.json");
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.readValue(jsonFile, ValuesModel.class));
}
}
class ValuesModelJsonDeserializer extends JsonDeserializer<List<ValueModel>> {
@Override
public List<ValueModel> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
final JsonDeserializer<Object> deserializer = ctxt.findRootValueDeserializer(SimpleType.constructUnsafe(ValueModel.class));
final JsonNode root = p.readValueAsTree();
// If node is a JSON object
if (root.isObject()) {
return Collections.singletonList(deserialize(p.getCodec(), root, deserializer, ctxt));
}
if (!root.isArray()) {
// value is null or primitive
return Collections.emptyList();
}
return StreamSupport.stream(root.spliterator(), false)
.map(this::unwrap)
.filter(node -> !node.isMissingNode())
.map(node -> deserialize(p.getCodec(), node, deserializer, ctxt))
.collect(Collectors.toList());
}
private JsonNode unwrap(JsonNode node) {
if (node.isArray()) {
if (node.isEmpty()) {
return MissingNode.getInstance();
}
return node.iterator().next();
}
return node;
}
private ValueModel deserialize(ObjectCodec codec, JsonNode value, JsonDeserializer<Object> valueDeser, DeserializationContext ctxt) {
try (JsonParser jsonParser = createNestedParser(codec, value)) {
return (ValueModel) valueDeser.deserialize(jsonParser, ctxt);
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
private JsonParser createNestedParser(ObjectCodec codec, JsonNode value) throws IOException {
TokenBuffer buffer = new TokenBuffer(codec, false);
codec.writeTree(buffer, value);
JsonParser parser = buffer.asParser();
parser.nextToken();
return parser;
}
}
To register custom deserialiser you can use @JsonDeserialize
annotation:
@JsonDeserialize(using = ValuesModelJsonDeserializer.class)
private List<ValueModel> values;