Как получить ссылку на картограф объектов Джексона в приложении jersey2/hk2

У меня есть приложение jersey2, настроенное для поддержки JSON через Jackson, добавив

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>${jersey.version}</version>
</dependency>

в файле POM и

public MyApplication() {
    ...
    register(JacksonFeature.class)
    ...
}

в моем приложении. Все работает, мои ресурсы получают десериализованные POJO в качестве аргументов

@POST @Consumes(MediaType.APPLICATION_JSON)
public void blah(MyPojo p) {
    ...
}

Теперь одному из этих ресурсов нужна ссылка на ObjectMapper Джексона, чтобы выполнить некоторую десериализацию самостоятельно. Я пытался сделать что-то вроде

@Inject
public MyResource(@Context ObjectMapper mapper) {
    ...
}

or

@GET
public String foo(@Context ObjectMapper mapper) {
    ...
}

но в обоих случаях ссылка на mapper пуста. Как добавить ссылку на ObjectMapper в свои ресурсы?


person agnul    schedule 15.05.2015    source источник


Ответы (2)


Во-первых, провайдер Джексона не использует ObjectMapper по умолчанию. На самом деле он вообще не использует ObjectMapper. Он использует другие API-интерфейсы Jackson для обработки (де) сериализации.

Если вы хотите использовать/внедрить один экземпляр ObjectMapper, вам нужно просто создать для него Factory

public class ObjectMapperFactory implements Factory<ObjectMapper> {

    final ObjectMapper mapper = new ObjectMapper();

    @Override
    public ObjectMapper provide() {
        return mapper;
    }

    @Override
    public void dispose(ObjectMapper t) {}   
}

Затем связать его

register(new AbstractBinder(){
    @Override
    public void configure() {
        bindFactory(ObjectMapperFactory.class)
            .to(ObjectMapper.class).in(Singleton.class);
    }
});

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

Еще одна вещь, которую следует отметить с поставщиком Джексона, заключается в том, что если мы предоставляем ContextResolver, например упомянутый @Laurentiu L, то Провайдер Джексона переключится на использование нашего домена ObjectMapper. В этом случае, если вы хотите использовать тот же ObjectMapper, вы можете найти его в файле Factory. Например

public class ObjectMapperFactory implements Factory<ObjectMapper> {

    private final Providers providers;
    final ObjectMapper mapper = new ObjectMapper();

    public ObjectMapperFactory(@Context Providers providers) {
        this.providers = providers;
    }

    @Override
    public ObjectMapper provide() {
        ContextResolver<ObjectMapper> resolver = providers.getContextResolver(
                ObjectMapper.class, MediaType.APPLICATION_JSON);
        if (resolver == null) { return mapper; }

        return resolver.getContext(null);
    }

    @Override
    public void dispose(ObjectMapper t) {}   
}

Чтобы все вышеперечисленное работало (используйте один ObjectMapper), вам нужно обязательно реализовать ContextResolver<ObjectMapper> и аннотировать ContextResolver соответствующими типами мультимедиа @Produces и @Consumes.

person Paul Samsotha    schedule 15.05.2015
comment
согласно документации Джексона, экземпляры ObjectMapper являются потокобезопасными, пока не происходит реконфигурация. Экземпляры Mapper полностью потокобезопасны при условии, что ВСЯ конфигурация экземпляра происходит до ЛЮБЫХ вызовов чтения или записи. fasterxml.github.io/jackson- привязка данных/javadoc/2.6/com/fasterxml/ - person Ricard Nàcher Roig; 14.06.2019
comment
@RicardNàcherRoig Разве я не это сказал? Следует отметить, что любая конфигурация ObjectMapper не является потокобезопасной. Если не правильно выразился, то я это и имел в виду. Вы не должны пытаться переконфигурировать преобразователь во время выполнения; Тот же преобразователь может использоваться для другого запроса. - person Paul Samsotha; 14.06.2019
comment
Спасибо @paul-samsotha за ответ. На мой взгляд, это неправильно сформулировано, или, по крайней мере, я думаю, что объяснение из документации API Джексона намного понятнее. Я потратил некоторое время, думая, что ObjectMapper никогда не был потокобезопасным. - person Ricard Nàcher Roig; 14.06.2019

Помимо JacksonFeature вам необходимо зарегистрировать ContextResolver для ObjectMapper.

Простой пример из Документации в разделе 9.1.4.2. Настроить и зарегистрироваться

@Provider
public class MyObjectMapperProvider implements ContextResolver<ObjectMapper> {

    final ObjectMapper defaultObjectMapper;

    public MyObjectMapperProvider() {
        defaultObjectMapper = createDefaultMapper();
    }

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

    private static ObjectMapper createDefaultMapper() {
        final ObjectMapper result = new ObjectMapper();
        result.configure(Feature.INDENT_OUTPUT, true);

        return result;
    }

    // ...
}

Полный пример кода доступен на Github

Вам также необходимо будет зарегистрировать его

        .register(MyObjectMapperProvider.class)  
person Laurentiu L.    schedule 15.05.2015
comment
Это работает, но, поскольку мне не нужно (пока?) специально предоставленное ObjectMapper, я надеялся найти способ использовать созданный Джерси вместо того, чтобы предоставлять свой собственный. - person agnul; 15.05.2015
comment
Если у кого-то возникла проблема: абстрактные типы либо необходимо сопоставить с конкретными типами с помощью Dropwizard, даже если вы зарегистрировали модуль с помощью addAbstractTypeMapping, это решение устранило проблему как шарм (просто зарегистрируйте свой модуль в createDefaultMapper и зарегистрируйте этот Provider с помощью environment.jersey().register() в run() вашего Application)! - person qwertzguy; 19.04.2017