正如其他人指出的那样,how it should work so it hasn't been implemented 没有达成共识。
如果你有类 Foo、Bar 和它们的父类 FooBar,当你有 JSON 时,解决方案似乎很明显:
{
"foo":<value>
}
或
{
"bar":<value>
}
但是当你得到时会发生什么并没有共同的答案
{
"foo":<value>,
"bar":<value>
}
乍一看,最后一个示例似乎是 400 Bad Request 的明显案例,但实际上有许多不同的方法:
- 将其作为 400 Bad Request 处理
- 类型/字段的优先级(例如,如果存在字段错误,则它的优先级高于某些其他字段 foo)
- 2 的更复杂情况。
我目前适用于大多数情况并尝试尽可能多地利用现有 Jackson 基础架构的解决方案是(每个层次结构只需要 1 个解串器):
public class PresentPropertyPolymorphicDeserializer<T> extends StdDeserializer<T> {
private final Map<String, Class<?>> propertyNameToType;
public PresentPropertyPolymorphicDeserializer(Class<T> vc) {
super(vc);
this.propertyNameToType = Arrays.stream(vc.getAnnotation(JsonSubTypes.class).value())
.collect(Collectors.toMap(Type::name, Type::value,
(a, b) -> a, LinkedHashMap::new)); // LinkedHashMap to support precedence case by definition order
}
@Override
public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
ObjectMapper objectMapper = (ObjectMapper) p.getCodec();
ObjectNode object = objectMapper.readTree(p);
for (String propertyName : propertyNameToType.keySet()) {
if (object.has(propertyName)) {
return deserialize(objectMapper, propertyName, object);
}
}
throw new IllegalArgumentException("could not infer to which class to deserialize " + object);
}
@SuppressWarnings("unchecked")
private T deserialize(ObjectMapper objectMapper,
String propertyName,
ObjectNode object) throws IOException {
return (T) objectMapper.treeToValue(object, propertyNameToType.get(propertyName));
}
}
示例用法:
@JsonSubTypes({
@JsonSubTypes.Type(value = Foo.class, name = "foo"),
@JsonSubTypes.Type(value = Bar.class, name = "bar"),
})
interface FooBar {
}
@AllArgsConstructor(onConstructor_ = @JsonCreator)
@Value
static class Foo implements FooBar {
private final String foo;
}
@AllArgsConstructor(onConstructor_ = @JsonCreator)
@Value
static class Bar implements FooBar {
private final String bar;
}
杰克逊配置
SimpleModule module = new SimpleModule();
module.addDeserializer(FooBar.class, new PresentPropertyPolymorphicDeserializer<>(FooBar.class));
objectMapper.registerModule(module);
或者如果您使用的是 Spring Boot:
@JsonComponent
public class FooBarDeserializer extends PresentPropertyPolymorphicDeserializer<FooBar> {
public FooBarDeserializer() {
super(FooBar.class);
}
}
测试:
@Test
void shouldDeserializeFoo() throws IOException {
// given
var json = "{\"foo\":\"foo\"}";
// when
var actual = objectMapper.readValue(json, FooBar.class);
// then
then(actual).isEqualTo(new Foo("foo"));
}
@Test
void shouldDeserializeBar() throws IOException {
// given
var json = "{\"bar\":\"bar\"}";
// when
var actual = objectMapper.readValue(json, FooBar.class);
// then
then(actual).isEqualTo(new Bar("bar"));
}
@Test
void shouldDeserializeUsingAnnotationDefinitionPrecedenceOrder() throws IOException {
// given
var json = "{\"bar\":\"\", \"foo\": \"foo\"}";
// when
var actual = objectMapper.readValue(json, FooBar.class);
// then
then(actual).isEqualTo(new Foo("foo"));
}
编辑:我在project 中添加了对此案例的支持以及更多内容。