Не думайте, что есть простое и чистое решение этой проблемы. Но вот некоторые мысли, как вы можете это решить (Gist demo для обоих случаев):
Вариант 1. Добавьте свойство @JsonIgnore
выше и свойство @JsonAnyGetter
в компонент верхнего уровня. Легко реализовать, но неудобно иметь статический ObjectMapper в bean-компоненте, и вам придется копировать этот код для каждого, имеющего свойство Parameter.
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();
}
}
}
Вариант 2: свойство @JsonIgnore
и регистрация настраиваемого сериализатора/десериализатора для корневого класса A
SimpleModule module = new SimpleModule();
module.addSerializer(A.class, new ASerializer());
module.addDeserializer(A.class, new ADeserializer());
mapper.registerModule(module);
Невозможно использовать @JsonSerialize
выше класса A, потому что сериализаторы и десериализаторы внутреннего ObjectMapper также будут использовать эту аннотацию, но вам нужно настроить ее на использование сериализатора/десериализатора по умолчанию, а не рекурсивно. Или вы можете реализовать что-то вроде https://stackoverflow.com/a/18405958/1032167, если вам действительно нужна аннотация
И сериализатор + десериализатор будет выглядеть примерно так (Неоптимизированный и просто доказательство концепции):
/************************ 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;
}
}
Общий десериализатор было бы довольно сложно написать. Но этот вопрос в любом случае касается сериализации в соответствии с телом вопроса.
17.10.2017