Как настроить механизм информации типа Джексона
В Джексоне я использую аннотацию @JsonTypeInfo, чтобы включить поддержку полиморфизма.
Если я не хочу использовать подход, основанный на аннотациях, я могу использовать глобальную типизацию по умолчанию или переопределить модуль обработки информации о типах.
Я пробовал информацию о глобальном типе, но он испускает информацию о типе для всех не окончательного типа
Что мне нужно,
- Я хочу включить информацию о типе только для полиморфного типа.
- Я хочу изменить формат информации о типе по умолчанию (на пару ключ-значение)
Можно ли достичь двух вышеупомянутых результатов, просто перевернув глобальную конфигурацию?
Если нет, то какую точку расширения я должен использовать, чтобы настроить модуль информации о типе? Я прочитал JacksonAnnotationIntrospector - класс, который имеет дело с информацией о типе.
Должен ли я настроить его для достижения вышеупомянутых двух пунктов?
Помощь с примером будет хорошо и хорошо.
3 ответа
Вы можете использовать Джексона DefaultTypeResolverBuilder
для этого. Расширить этот класс и переопределить useForType
метод соответственно. Вот пример, который добавляет информацию о типе только для классов, принадлежащих test.jackson
пакет (и подпакеты):
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTypeResolverBuilder;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
public class CustomTypeResolverBuilder extends DefaultTypeResolverBuilder
{
public CustomTypeResolverBuilder()
{
super(DefaultTyping.NON_FINAL);
}
@Override
public boolean useForType(JavaType t)
{
if (t.getRawClass().getName().startsWith("test.jackson")) {
return true;
}
return false;
}
}
Теперь рассмотрим, что у вас есть Foo.java
в test.jackson
пакет и Bar.java
в org.myorg
пакет, каждый из которых содержит int
переменная называется "целое число" и String
переменная называется "строка".
Вы можете сериализовать объекты этих двух классов следующим образом:
ObjectMapper objectMapper = new ObjectMapper();
TypeResolverBuilder<?> typeResolver = new CustomTypeResolverBuilder();
typeResolver.init(JsonTypeInfo.Id.CLASS, null);
typeResolver.inclusion(JsonTypeInfo.As.PROPERTY);
typeResolver.typeProperty("@CLASS");
objectMapper.setDefaultTyping(typeResolver);
Foo foo = new Foo(10, "Foo");
Bar bar = new Bar(20, "Bar");
System.out.println(objectMapper.writeValueAsString(foo));
System.out.println(objectMapper.writeValueAsString(bar));
Соответствующий вывод будет:
{"@CLASS":"test.jackson.Foo","integer":10,"string":"Foo"}
{"integer":20,"string":"Bar"}
Вы также можете настроить имя атрибута, который представляет тип ("@CLASS" в приведенном выше примере). Надеюсь это поможет!
Вы можете использовать библиотеку Moonwlker.
С его помощью вы можете создать ObjectMapper
нравится:
ObjectMapper objectMapper = new ObjectMapper();
MoonwlkerModule module =
MoonwlkerModule.builder()
.fromProperty("@CLASS").toSubclassesOf(Animal.class)
.build();
objectMapper.registerModule(module);
А затем используйте этот сопоставитель для (де) сериализации. Веб-сайт Moonwlker содержит более подробную информацию и параметры конфигурации.
private static final ObjectMapper objectMapper = new ObjectMapper() {{
this.setSerializationInclusion(JsonInclude.Include.NON_NULL);
this.setVisibility(this.getSerializationConfig()
.getDefaultVisibilityChecker()
.withFieldVisibility(JsonAutoDetect.Visibility.ANY)
.withGetterVisibility(JsonAutoDetect.Visibility.NONE)
.withSetterVisibility(JsonAutoDetect.Visibility.NONE)
.withCreatorVisibility(JsonAutoDetect.Visibility.NONE));
this.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
this.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
this.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
this.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);
this.addMixIn(Throwable.class, ThrowableMixIn.class);
TypeResolverBuilder<?> mapTyper = new DefaultTypeResolverBuilder(DefaultTyping.NON_FINAL) {
public boolean useForType(JavaType t) {
switch (_appliesFor) {
case NON_CONCRETE_AND_ARRAYS:
while (t.isArrayType()) {
t = t.getContentType();
}
// fall through
case OBJECT_AND_NON_CONCRETE:
return (t.getRawClass() == Object.class) || !t.isConcrete();
case NON_FINAL:
while (t.isArrayType()) {
t = t.getContentType();
}
// to fix problem with wrong long to int conversion
if (t.getRawClass() == Long.class) {
return true;
}
if (t.getRawClass() == XMLGregorianCalendar.class) {
return false;
}
return !t.isFinal(); // includes Object.class
default:
// case JAVA_LANG_OBJECT:
return t.getRawClass() == Object.class;
}
}
};
mapTyper.init(JsonTypeInfo.Id.CLASS, null);
mapTyper.inclusion(JsonTypeInfo.As.PROPERTY);
this.setDefaultTyping(mapTyper);
}};
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "@id")
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY,
getterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY,
setterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
public static class ThrowableMixIn {
}