正如其他人指出的那样,关于它应该如何工作,因此尚未实施,没有共识。
如果你有类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));
}
@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 {
var json = "{\"foo\":\"foo\"}";
var actual = objectMapper.readValue(json, FooBar.class);
then(actual).isEqualTo(new Foo("foo"));
}
@Test
void shouldDeserializeBar() throws IOException {
var json = "{\"bar\":\"bar\"}";
var actual = objectMapper.readValue(json, FooBar.class);
then(actual).isEqualTo(new Bar("bar"));
}
@Test
void shouldDeserializeUsingAnnotationDefinitionPrecedenceOrder() throws IOException {
var json = "{\"bar\":\"\", \"foo\": \"foo\"}";
var actual = objectMapper.readValue(json, FooBar.class);
then(actual).isEqualTo(new Foo("foo"));
}
编辑:我已经在这个项目中添加了对此情况和更多情况的支持。