Не удается получить Джерси 2 приложение для использования пользовательских Джексон XmlMapper
Я пишу веб-приложение, используя Jersey 2.22.1 и Jackson 2.6.3. Мой пом.xml выглядит следующим образом:
<!-- JERSEY -->
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.22.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.22.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.22.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>2.22.1</version>
</dependency>
<dependency>
<groupId>asm</groupId>
<artifactId>asm</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>woodstox-core-asl</artifactId>
<version>4.4.1</version>
</dependency>
<!-- JACKSON -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.6.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.6.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.6.3</version>
</dependency>
Цель состоит в том, чтобы использовать различные пользовательские ObjectMappers для отображения JSON и XML. Я создал два класса провайдеров: JSONMapperProvider
@Provider
@Produces(MediaType.APPLICATION_JSON)
public class JSONMapperProvider implements ContextResolver<ObjectMapper> {
private static ObjectMapper objectMapper;
public JSONMapperProvider() {
init();
}
public static void init() {
if (objectMapper == null) {
objectMapper = new ObjectMapper();
// SERIALIZATION
objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true)
.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false)
.setSerializationInclusion(Include.NON_NULL)
.setSerializationInclusion(Include.NON_EMPTY);
// DESERIALIZATION
objectMapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true)
.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
// set up ISO 8601 date/time stamp format:
ObjectMapperProvider.DATE_FORMAT_ISO8601.setTimeZone(TimeZone.getTimeZone("GMT"));
objectMapper.setDateFormat(ObjectMapperProvider.DATE_FORMAT_ISO8601);
// Custom deserializer for date which helps deserialization of date
// without time
SimpleModule dateDeserializerModule = new SimpleModule("DateDeserializerModule", Version.unknownVersion());
dateDeserializerModule.addDeserializer(Date.class, new CustomJsonDateDeserializer());
//objectMapper.registerModule(dateDeserializerModule);
}
}
@Override
public ObjectMapper getContext(Class<?> type) {
System.out.println("JSONMapperProvider.getContext() called with type: "
+ type);
return objectMapper;
}
private static class CustomJsonDateDeserializer extends JsonDeserializer<Date> {
@Override
public Date deserialize(JsonParser jsonparser, DeserializationContext deserializationcontext) throws IOException, JsonProcessingException {
String date = jsonparser.getText();
try {
return DateUtil.parseDate(date);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
}
И XMLMapperProvider
@Provider
@Produces({ MediaType.APPLICATION_XML })
public class XMLMapperProvider implements ContextResolver<ObjectMapper> {
private static ObjectMapper objectMapper;
public XMLMapperProvider() {
init();
}
public static void init() {
if (objectMapper == null) {
JacksonXmlModule module = new JacksonXmlModule();
module.setDefaultUseWrapper(false);
objectMapper = new XmlMapper(module);
// SERIALIZATION
objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true)
.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false)
.setSerializationInclusion(Include.NON_NULL)
.setSerializationInclusion(Include.NON_EMPTY);
// DESERIALIZATION
objectMapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true)
.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
// set up ISO 8601 date/time stamp format:
ObjectMapperProvider.DATE_FORMAT_ISO8601.setTimeZone(TimeZone.getTimeZone("GMT"));
objectMapper.setDateFormat(ObjectMapperProvider.DATE_FORMAT_ISO8601);
// Custom deserializer for date which helps deserialization of date
// without time
SimpleModule dateDeserializerModule = new SimpleModule("DateDeserializerModule", Version.unknownVersion());
dateDeserializerModule.addDeserializer(Date.class, new CustomJsonDateDeserializer());
objectMapper.registerModule(dateDeserializerModule);
}
}
@Override
public ObjectMapper getContext(Class<?> type) {
System.out.println("XMLMapperProvider.getContext() called with type: "
+ type);
return objectMapper;
}
private static class CustomJsonDateDeserializer extends JsonDeserializer<Date> {
@Override
public Date deserialize(JsonParser jsonparser, DeserializationContext deserializationcontext) throws IOException, JsonProcessingException {
String date = jsonparser.getText();
try {
return DateUtil.parseDate(date);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
}
Оба они находятся в одном пакете, который зарегистрирован в классе ресурсов приложения
@ApplicationPath("resources")
public class CCRestResources extends ResourceConfig {
public CCRestResources() {
register(JacksonFeature.class);
packages("com.cc.rest.jersey");
}
}
Если я сделаю запрос GET к методу, который @производит (MediaType.APPLICATION_JSON) все отлично, и используется мой пользовательский картограф. Но если я сделаю такой же запрос к emthod, который @производит (MediaType.APPLICATION_XML) приложение использует какой-то маппер по умолчанию, а не мой пользовательский. Пожалуйста, помогите, если у вас есть какие-либо идеи. Спасибо!
1 ответ:
Итак, несколько вещей. Во-первых, вам нужно больше, чем просто основная зависимость Джексона xml, вам нужен фактический поставщик jaxrs
<dependency> <groupId>com.fasterxml.jackson.jaxrs</groupId> <artifactId>jackson-jaxrs-xml-provider</artifactId> <version>${jackson2.version}</version> </dependency>
Тогда вы должны исключить поставщика JAXB, который является поставщиком по умолчанию, используемым Джерси. (У меня не было проблем с тем, чтобы оставить его во время тестирования, но если вы не собираетесь его использовать, я бы просто исключил его). Он притягивается
jersey-server
, поэтому вы должны явно объявитьjersey-server
и исключитьjersey-media-jaxb
из него<dependency> <groupId>org.glassfish.jersey.core</groupId> <artifactId>jersey-server</artifactId> <version>${jersey2.version}</version> <exclusions> <exclusion> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-jaxb</artifactId> </exclusion> </exclusions> </dependency>
Тогда вам нужно будет зарегистрировать
JacksonJaxbXMLProvider
(или просто ...JacksonXMLProvider
Если вы не хотите или не планируете использовать аннотации JAXB).public CCRestResources() { register(JacksonFeature.class); register(JacksonJaxbXMLProvider.class); packages("com.cc.rest.jersey"); }
Затем, наконец, вам нужно параметризовать
ContextResolver
как типXmlMapper
, а неObjectMapper
. Как видно здесь , поставщик ищетContextResolver
дляXmlMapper
, а неObjectMapper
.