Регистрация JacksonJsonProvider с ObjectMapper + JavaTimeModule для клиента Jersey 2
Я пытаюсь упорядочить ответ, содержащий метку времени в формате ISO, например:
{
...
"time" : "2014-07-02T04:00:00.000000Z"
...
}
в ZonedDateTime
поле в моей модели предметной области. В конце концов это работает, если я использую решение, которое прокомментировано в следующем фрагменте. Есть много похожих вопросов по SO, но я хотел бы получить конкретный ответ, что не так с другим подходом, который использует JacksonJsonProvider
с ObjectMapper + JavaTimeModule
?
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
JacksonJsonProvider provider = new JacksonJsonProvider(mapper);
Client client = ClientBuilder.newBuilder()
// .register(new ObjectMapperContextResolver(){
// @Override
// public ObjectMapper getContext(Class<?> type) {
// ObjectMapper mapper = new ObjectMapper();
// mapper.registerModule(new JavaTimeModule());
// return mapper;
// }
// })
.register(provider)
.register(JacksonFeature.class)
.build();
Ошибка, которую я получаю:
javax.ws.rs.client.ResponseProcessingException: com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of java.time.ZonedDateTime: no String-argument constructor/factory method to deserialize from String value ('2017-02-24T20:46:05.000000Z')
at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream@53941c2f
Зависимости проекта:
compile 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.8.7'
compile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.8.7'
compile 'com.fasterxml.jackson.core:jackson-core:2.8.7'
compile 'com.fasterxml.jackson.core:jackson-databind:2.8.7'
compile 'com.fasterxml.jackson.core:jackson-annotations:2.8.7'
compile 'org.glassfish.jersey.core:jersey-client:2.25.1'
редактировать
Десериализация происходит здесь:
CandlesResponse<BidAskCandle> candlesResponse = webTarget.request()
.header(HttpHeaders.AUTHORIZATION,"Bearer "+token)
.accept(MediaType.APPLICATION_JSON)
.get(new GenericType<CandlesResponse<BidAskCandle>>(){});
3 ответа
В конце концов это работает, если я использую решение, которое прокомментировано в следующем фрагменте.
Прежде всего, вам не хватает зависимости в вашем списке, которая у вас также есть, что является проблемой.
jersey-media-json-jackson
Этот модуль зависит от собственного модуля Джексона, который имеет JacksonJsonProvider
, Когда вы регистрируете JacksonFeature
(что идет с jersey-media-json-jackson
), он регистрирует свой собственный JacksonJaxbJsonProvider
, который, кажется, имеет приоритет над любым, что вы предоставляете.
Когда вы используете ContextResolver
, JacksonJsonProvider
на самом деле выглядит, что ContextResolver
и использует его для разрешения ObjectMapper
, Вот почему это работает. Использовали ли вы JacksonFeature
или зарегистрировал свой JacksonJsonProvider
(без настройки ObjectMapper
за это) ContextResovler
должно сработать.
Еще одна вещь о jersey-media-json-jackson
модуль, он участвует в механизме автоматического обнаружения Джерси, который регистрирует его JacksonFeature
, Поэтому, даже если вы не зарегистрировали его явно, он все равно будет зарегистрирован. Единственные способы избежать его регистрации:
- Отключите авто-обнаружение (как упомянуто в предыдущей ссылке)
- Не используйте
jersey-media-json-jackson
, Просто используйте родной модуль Джексонаjackson-jaxrs-json-provider
, Дело в том, что это то, чтоjersey-media-json-jackson
добавляет пару функций поверх нативного модуля, так что вы их потеряете. Не проверял, но кажется, что если вы используете
JacksonJaxbJsonProvider
вместоJacksonJsonProvider
это может сработать. Если вы посмотрите на источник дляJacksonFeature
, вы увидите, что он проверяет уже зарегистрированныйJacksonJaxbJsonProvider
, Если он есть, он не будет регистрировать свой собственный.Единственное, в чем я не уверен, это автоматическое обнаружение. Порядок, в котором он зарегистрирован, если это повлияет на то, поймет ли он ваш зарегистрированный
JacksonJaxbJsonProvider
, То, что вы можете проверить.
Из моего любимого проекта:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.version}</version>
</dependency>
public WebTarget getTarget(URI uri) {
Client client = ClientBuilder
.newClient()
.register(JacksonConfig.class);
return client.target(uri);
}
где
@Provider
public class JacksonConfig implements ContextResolver<ObjectMapper> {
private final ObjectMapper objectMapper;
public JacksonConfig() {
objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
}
@Override
public ObjectMapper getContext(Class<?> aClass) {
return objectMapper;
}
}
Конфигурация Джексона выглядит отлично, я попробовал следующее и смог десериализовать значение:
public class Test {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
Model model = mapper.readValue("{\"time\" : \"2014-07-02T04:00:00.000000Z\"}", Model.class);
System.out.println(model.getTime());
}
}
class Model{
private ZonedDateTime time;
public ZonedDateTime getTime() {
return time;
}
public void setTime(ZonedDateTime time) {
this.time = time;
}
}
Я могу воспроизвести это, комментируя mapper.registerModule(new JavaTimeModule());
, Итак, похоже, клиент-джерси не использует кастом mapper
пример. Не могли бы вы попытаться настроить его, как описано здесь.