I have the following two types of JSON objects:
{"foo": "String value"}
and
{"bar": "String value"}
Both of them represent a specialized type of the same base object. How can I use Jackson for deserializing them ? The type information is only represented by the keys themselves and not the value for any key (almost all examples use the value of the key for determining the type : https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization)
@JsonProperty can change the visibility of logical property using its access element during serialization and deserialization of JSON. @JsonAlias defines one or more alternative names for a property to be accepted during deserialization.
How to deserialize Date from JSON using Jackson. In order to correct deserialize a Date field, you need to do two things: 1) Create a custom deserializer by extending StdDeserializer<T> class and override its deserialize(JsonParser jsonparser, DeserializationContext context) method.
The simple readValue API of the ObjectMapper is a good entry point. We can use it to parse or deserialize JSON content into a Java object. Also, on the writing side, we can use the writeValue API to serialize any Java object as JSON output.
Jackson doesn't offer an out of the box solution for that, but it doesn't mean that you are out of luck.
Assuming that your classes implement a common interface or extend a common class, as shown below:
public interface Animal {
}
public class Dog implements Animal {
private String bark;
// Default constructor, getters and setters
}
public class Cat implements Animal {
private String meow;
// Default constructor, getters and setters
}
You can create a custom deserializer based on the property name. It allows you to define a unique property that will be used to look up the class to perform the deserialization to:
public class PropertyBasedDeserializer<T> extends StdDeserializer<T> {
private Map<String, Class<? extends T>> deserializationClasses;
public PropertyBasedDeserializer(Class<T> baseClass) {
super(baseClass);
deserializationClasses = new HashMap<String, Class<? extends T>>();
}
public void register(String property, Class<? extends T> deserializationClass) {
deserializationClasses.put(property, deserializationClass);
}
@Override
public T deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
ObjectMapper mapper = (ObjectMapper) p.getCodec();
JsonNode tree = mapper.readTree(p);
Class<? extends T> deserializationClass = findDeserializationClass(tree);
if (deserializationClass == null) {
throw JsonMappingException.from(ctxt,
"No registered unique properties found for polymorphic deserialization");
}
return mapper.treeToValue(tree, deserializationClass);
}
private Class<? extends T> findDeserializationClass(JsonNode tree) {
Iterator<Entry<String, JsonNode>> fields = tree.fields();
Class<? extends T> deserializationClass = null;
while (fields.hasNext()) {
Entry<String, JsonNode> field = fields.next();
String property = field.getKey();
if (deserializationClasses.containsKey(property)) {
deserializationClass = deserializationClasses.get(property);
break;
}
}
return deserializationClass;
}
}
Then instantiate and configure the deserializer:
PropertyBasedDeserializer<Animal> deserializer =
new PropertyBasedDeserializer<>(Animal.class);
deserializer.register("bark", Dog.class); // If "bark" is present, then it's a Dog
deserializer.register("meow", Cat.class); // If "meow" is present, then it's a Cat
Add it to a module:
SimpleModule module = new SimpleModule("custom-deserializers", Version.unknownVersion());
module.addDeserializer(Animal.class, deserializer);
Register the module and perform the deserialization as usual:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
String json = "[{\"bark\":\"bowwow\"}, {\"bark\":\"woofWoof\"}, {\"meow\":\"meeeOwww\"}]";
List<Animal> animals = mapper.readValue(json, new TypeReference<List<Animal>>() { });
With fasterxml jackson
, you can do this:
abstract class FooOrBar {
companion object {
@JvmStatic
@JsonCreator
private fun creator(json: Map<String, String>): FooOrBar? {
return when {
json.containsKey("foo") -> Foo(json["foo"] as String)
json.containsKey("bar") -> Foo(json["bar"] as String)
else -> null
}
}
}
}
class Foo(val foo: String) : FooOrBar() // even can use map delegate if you know what it is
class Bar(val bar: String) : FooOrBar()
It is Kotlin, but you will get the idea.
Note the
@JsonCreator
is used. the annotated creator
function has single argument (which is one kind of the two signatures required by JsonCreator
), JSON is deserialized as a Map
instance and passed to the creator
. From here, you can create your class instance.
----------------UPDATE-------------------------
You can also use JsonNode
for the creator
function for nested and complex JSON.
private fun creator(json: JsonNode): FooOrBar?
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