Как настроить механизм информации типа Джексона

В Джексоне я использую аннотацию @JsonTypeInfo, чтобы включить поддержку полиморфизма.

Если я не хочу использовать подход, основанный на аннотациях, я могу использовать глобальную типизацию по умолчанию или переопределить модуль обработки информации о типах.

Я пробовал информацию о глобальном типе, но он испускает информацию о типе для всех не окончательного типа

Что мне нужно,

  1. Я хочу включить информацию о типе только для полиморфного типа.
  2. Я хочу изменить формат информации о типе по умолчанию (на пару ключ-значение)

Можно ли достичь двух вышеупомянутых результатов, просто перевернув глобальную конфигурацию?

Если нет, то какую точку расширения я должен использовать, чтобы настроить модуль информации о типе? Я прочитал 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 {

}
Другие вопросы по тегам