Мне нужно написать собственный десериализатор для класса с дженериками. Я не мог найти способ сделать это, однако не могу представить, что я единственный, у кого эта проблема. Насколько я понял, есть два способа реализовать это, но ни один из них не реализуем:
- Предоставление параметра класса десериализатору в конструкторе десериализатора не работает, потому что при регистрации десериализатора связь между Type.class и экземпляром десериализатора с переданным экземпляром класса теряется.
Например:
public class Foo<T> {}
public class FooDeserializer<T> {
public FooDeserializer(Class<T> type) { ... }
...
}
// Boilerplate code...
module.addDeserializer(Foo.class, new FooDeserializer<Bar1>(Bar1.class));
module.addDeserializer(Foo.class, new FooDeserializer<Bar2>(Bar2.class));
Это не работает, когда экземпляр ObjectMapper получает экземпляр Foo, информация о типе универсального параметра недоступна (стирание типа), поэтому он просто выбирает последний зарегистрированный десериализатор.
- Сохранение ссылки на универсальный тип в классе не помогает, поскольку созданная версия класса не может быть передана десериализатору (интерфейс - readValue (String, Class)).
Например:
String json = "...";
ObjectMapper mapper = ...;
Foo<Bar1> foo = new Foo<>(Bar1.class);
foo = mapper.readValue(json, Foo.class); // Can't pass empty foo instance with Class<?> field containing Bar1.class
Потребуется что-то вроде этого:
mapper.readValue(json, Foo.class, Bar1.class); // Doesn't exist in jackson
Есть предложения, как это сделать?
РЕДАКТИРОВАТЬ: Я нашел способ решить проблему, но это не чистое решение:
Я расширяю FooDeserializer полем Class, чтобы сохранить тип универсального параметра Foo. Затем, каждый раз, когда я хочу десериализовать некоторый json в новый экземпляр Foo, я получаю новый экземпляр ObjectMapper (я использую ObjectMapper # copy на предварительно настроенном экземпляре с фабрики) и передаю ему новый модуль, который содержит экземпляр FooDeserializer с параметр класса (на данный момент я знаю тип). Module, FooDeserializer и копия ObjectMapper недолговечны, они создаются только для этого единственного действия десериализации. Как я уже сказал, не очень чисто, но все же лучше, чем многократно создавать подклассы Foo и писать десериализатор для каждого.
Пример:
public class FooDeserializer<T> extends StdDeserializer<T> {
private Class<T> type;
public FooDeserializer(Class<T> type) { this.type = type }
...
}
// Meanwhile, before deserialization:
ObjectMapper mapper = MyObjectMapperFactory.get().copy();
SimpleModule module = new SimpleModule(new Version(0,0,1,null,null,null);
module.addDeserializer(Foo.class, new FooDeserializer(Bar1.class);
mapper.addModule(module);
Foo<Bar1> x = mapper.readValue(json, Foo.class);
Вероятно, поместив это в служебный метод, чтобы скрыть уродство.