I supposed that I had a parent Class Parameter that has 2 sub classes ComboParameter and IntegerParameter
@JsonSubTypes({
@JsonSubTypes.Type(value = IntegerParameter.class, name = "integerParam"),
@JsonSubTypes.Type(value = ComboParameter.class, name = "comboParam")
})
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = As.WRAPPER_OBJECT)
public abstract class Parameter {
String regEx;
}
@JsonTypeName("integerParam")
public class IntegerParameter extends Parameter {
}
@JsonTypeName("comboParam")
public class ComboParameter extends Parameter {
List<String> values;
}
And I have a class that had an attribute parameter
class A {
@JsonUnwrapped
Parameter parameter;
}
The Serialization of an Object A
throw an Exception
com.fasterxml.jackson.databind.JsonMappingException: Unwrapped property requires use of type information: can not serialize without disabling
SerializationFeature.FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS
And if I delete the annotation @JsonUnwrapped
I will have a json like That
{
parameter:{
integerParam:{
regEx: regExVal
}
}
}
And what I need is a json like that:
{
integerParam:{
regEx: regExVal
}
}
NB I' am using Jackson 2.4.4
Don't think there is easy and clean solution for this problem. But here is some thoughts how you can solve it (Gist demo for both cases):
Option 1: Add @JsonIgnore
above property and @JsonAnyGetter
in the top level bean. Easy to implement but not nice to have static ObjectMapper in bean and will have to copy this code to every been with Parameter property
public class A {
@JsonIgnore
Parameter parameter;
// can put ObjectMapper and most of this code in util class
// and just use JacksonUtils.toMap(parameter) as return of JsonAnyGetter
private static ObjectMapper mapper = new ObjectMapper();
/************************ Serialization ************************/
@JsonAnyGetter
private Map<String, Object> parameterAsMap(){
return mapper.convertValue(parameter, Map.class);
}
/************************ Deserialization **********************/
@JsonAnySetter
private void parameterFromMap(String key, JsonNode value) {
try {
parameter = mapper.readValue(String.format("{\"%s\":%s}", key,value),
Parameter.class);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Option 2: @JsonIgnore
property and register custom serializer/deserializer for root A
class
SimpleModule module = new SimpleModule();
module.addSerializer(A.class, new ASerializer());
module.addDeserializer(A.class, new ADeserializer());
mapper.registerModule(module);
Can't use @JsonSerialize
above A class, because serializers and deserializers inner ObjectMapper will also use this annotation, but you need to setup it to use default serializer/deserializer and not itself recursively. Or you could implement something like https://stackoverflow.com/a/18405958/1032167 if your really want the annotation
And serializer + deserializer would look something like this (Unoptimized and just a prove of concept):
/************************ Serialization ************************/
public static class ASerializer extends JsonSerializer<A> {
private static ObjectMapper m = new ObjectMapper();
@Override
public void serialize(A value, JsonGenerator gen,
SerializerProvider serializers) throws IOException {
Map defaults = m.convertValue(value, Map.class);
Map params = m.convertValue(value.getParameter(), Map.class);
defaults.putAll(params);
gen.writeObject(defaults);
}
}
/************************ Deserialization **********************/
public static class ADeserializer extends JsonDeserializer<A> {
private static ObjectMapper m = new ObjectMapper();
private static String[] subtipes = {"integerParam", "comboParam"};
public ADeserializer() {
m.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
}
@Override
public A deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {
TreeNode node = m.readTree(p);
A a = m.convertValue(node, A.class);
// hardcoded , probably can be done dynamically
// with annotations inspection
for (String key : subtipes) {
TreeNode value = node.get(key);
if (value != null) {
String json = String.format("{\"%s\":%s}", key, value);
a.setParameter(m.readValue(json, Parameter.class));
break;
}
}
return a;
}
}
Generic deserializer would be pretty hard to write. But this question is about serialization anyway according to the question body.
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