Использование Джексона в Джерси с несколькими настроенными ObjectMappers

Можно ли настроить Джерси с помощью Джексона для сериализации/десериализации с использованием нескольких настроенных ObjectMappers?

Что я хотел бы сделать, так это зарегистрировать Джексона «по умолчанию» ObjectMapper, а затем иметь возможность зарегистрировать другую функцию, которая предоставляет ObjectMapper с некоторой специализированной конфигурацией, которая при определенных обстоятельствах «отменит» «по умолчанию» ObjectMapper.

Например, это ContextResolver будет для картографа «по умолчанию»:

@Provider
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class JacksonMapperProvider implements ContextResolver<ObjectMapper> {
    private final ObjectMapper mObjectMapper;

    public JacksonMapperProvider() {
        mObjectMapper = createMapper();
    }

    protected abstract ObjectMapper createMapper() {
        ObjectMapper mapper = createMapper();

        return mapper
            .setSerializationInclusion(Include.ALWAYS)
            .configure(JsonParser.Feature.ALLOW_COMMENTS, true)
            .configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true)
            .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true)
            .configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
    }

    @Override
    public ObjectMapper getContext(Class<?> type) {
        return mObjectMapper;
    }
}

И этот ContextResolver должен был переопределить маппер «по умолчанию»:

@Provider
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class SpecializedMapperProvider implements ContextResolver<ObjectMapper> {
    private final ObjectMapper mObjectMapper;

    public SpecializedMapperProvider() {
        mObjectMapper = createMapper();
    }

    protected abstract ObjectMapper createMapper() {
        ObjectMapper mapper = createMapper();

        return mapper
            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
            .setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"))
            .registerModule(new SpecializedModule1())
            .registerModule(new SpecializedModule2());
    }

    @Override
    public ObjectMapper getContext(Class<?> type) {
        if(SomeType.isAssignableFrom(type)) {
            return mObjectMapper;
        }
        return null;
    }
}

Я вижу в коде JacksonJsonProvider, что Джексон поддерживает внедрение/разрешение провайдера ObjectMapper. Однако на практике я вижу, что «порядок» поставщиков кажется случайным (я предполагаю, что это не так, но я не могу разобраться, как контролировать порядок). Иногда «переопределение» предшествует «по умолчанию» и все работает, но при следующем запуске сервера порядок меняется.

Я пытался заставить это работать несколькими способами, включая:

  • Регистрация ContextResolver<ObjectMapper> реализаций вручную (в разном порядке)
  • Регистрация реализаций ContextResolver<ObjectMapper> с помощью аннотаций @Provider
  • Указание приоритета при регистрации

Я использую следующее:

  • Джерси 2.8
  • Джексон 2.3.3

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


person cmatthews.dickson    schedule 12.05.2014    source источник
comment
Я обнаружил, что если у вас есть ObjectMapper defaultMapper = return new ObjectMapper(), это не то же самое, что экземпляр JacksonJsonProvider по умолчанию. По крайней мере, не для JacksonJaxbJsonProvider.   -  person ulab    schedule 11.04.2016


Ответы (2)


Вы можете настроить порядок провайдеров, но на самом деле лучше всего использовать в этой ситуации одного провайдера:

@Provider
public class JacksonMapperProvider implements ContextResolver<ObjectMapper> {
    private final ObjectMapper defaultMapper;
    private final ObjectMapper specializedMapper;

    public JacksonMapperProvider() {
        defaultMapper = createDefaultMapper();
        specializedMapper = createSpecializedMapper();
    }

    private static ObjectMapper createDefaultMapper() {
        return new ObjectMapper()
            .setSerializationInclusion(Include.ALWAYS)
            .configure(JsonParser.Feature.ALLOW_COMMENTS, true)
            .configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true)
            .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true)
            .configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
    }

    private static ObjectMapper createSpecializedMapper() {
        return new ObjectMapper()
            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
            .setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"))
            .registerModule(new SpecializedModule1())
            .registerModule(new SpecializedModule2());
    }

    @Override
    public ObjectMapper getContext(Class<?> type) {
        if (SomeType.isAssignableFrom(type)) {
            return specializedMapper;
        }
        else {
            return defaultMapper;
        }
    }
}
person Alden    schedule 12.05.2014
comment
Спасибо. Это то, что я сделал изначально, чтобы заставить его работать, однако я надеялся сохранить вещи отдельными и независимыми от разных модулей/областей приложения и избежать потенциального мега-провайдера, который может возникнуть из-за необходимости разных картографов. Не могли бы вы пояснить, почему в данной ситуации лучше использовать одного поставщика? - person cmatthews.dickson; 13.05.2014
comment
У меня была аналогичная проблема, усугубляемая тем, что мы хотели использовать MixIns для аннотаций Джексона, а не помещать их в настоящие классы. Мы решили нашу проблему, создав собственную аннотацию @JsonMixin, а затем просканировав все аннотированные объекты во время выполнения. Вы можете сделать что-то подобное для динамической настройки ObjectMapper и поддерживать только один. - person Baldy; 15.05.2014

Самый новый способ

new ObjectMapper().configure(com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);

чтобы получить ALLOW_UNQUOTED_FIELD_NAMES в последних версиях jackson.

person Faraz    schedule 16.12.2019