2016-11-11 3 views
3

Я использую RESTEasy 3.1.0.CR3 с Tomcat 8.5.6 внутри Eclipse 4.6.1 с JSBoss resteasy-jackson2-provider. У меня есть простой JavaBean FooBar, который возвращает строку «ID» и «путь» java.nio.file.Path.Jackson 2 неправильно сериализует Java java.nio.file.Path

Jackson упрощает сериализацию в JSON. В моем ресурсе JAX-RS я просто указываю @Produces("application/json; charset=UTF-8"). Но Джексон не использует Path.toString(). Вместо этого, как представляется, с помощью Path.toURI().toString() или что-то:!

{ 
    "id": "foo", 
    "path": "file:///C:/Users/jdoe/bar" 
} 

Почему ?? И что еще более важно, как я могу заставить Джексона просто использовать toString() версию Path?

Вот мое дерево зависимостей проекта:

+- com.google.code.findbugs:jsr305:jar:3.0.1:provided 
+- com.google.guava:guava:jar:20.0:compile 
+- javax.ws.rs:javax.ws.rs-api:jar:2.0.1:provided 
+- org.jboss.resteasy:resteasy-jaxrs:jar:3.1.0.CR3:compile 
| +- org.jboss.spec.javax.ws.rs:jboss-jaxrs-api_2.0_spec:jar:1.0.1.Beta1:compile 
| +- org.jboss.resteasy:resteasy-jaxrs-services:jar:3.1.0.CR3:compile 
| +- org.jboss.spec.javax.annotation:jboss-annotations-api_1.2_spec:jar:1.0.0.Final:compile 
| +- javax.activation:activation:jar:1.1.1:compile 
| +- org.apache.httpcomponents:httpclient:jar:4.5.2:compile 
| | +- org.apache.httpcomponents:httpcore:jar:4.4.4:compile 
| | +- commons-logging:commons-logging:jar:1.2:compile 
| | \- commons-codec:commons-codec:jar:1.9:compile 
| +- commons-io:commons-io:jar:2.5:compile 
| +- net.jcip:jcip-annotations:jar:1.0:compile 
| \- org.jboss.logging:jboss-logging:jar:3.3.0.Final:compile 
+- org.jboss.resteasy:resteasy-servlet-initializer:jar:3.1.0.CR3:compile 
+- org.jboss.resteasy:resteasy-jackson2-provider:jar:3.1.0.CR3:compile 
| +- com.fasterxml.jackson.core:jackson-core:jar:2.8.3:compile 
| +- com.fasterxml.jackson.core:jackson-databind:jar:2.8.3:compile 
| +- com.fasterxml.jackson.core:jackson-annotations:jar:2.8.3:compile 
| \- com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:jar:2.8.3:compile 
|  +- com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:jar:2.8.3:compile 
|  \- com.fasterxml.jackson.module:jackson-module-jaxb-annotations:jar:2.8.3:compile 
+- junit:junit:jar:4.12:test 
| \- org.hamcrest:hamcrest-core:jar:1.3:test 
\- org.hamcrest:hamcrest-library:jar:1.3:test 

Обратите внимание, что это не приемлемо для меня, чтобы добавить аннотации к моему FooBar класса, который является классом модели предметной области, что не должно быть никакой связи с RESTful Сведения о сериализации API.

Я хочу простой способ подключиться к Jackson2 в RESTEasy и изменить сериализацию значения свойства Path без изменения моего класса или написания пользовательского сериализатора для моего класса.

+0

Вы можете разделить Джексона версию, потому что по крайней мере 2.7.0 использует ToStringSerializer сериализовать Path. – Shashank

+0

Я использую ['org.jboss.resteasy: resteasy-jackson2-provider: 3.1.0.CR3'] (http://search.maven.org/#artifactdetails%7Corg.jboss.resteasy%7Cresteasy-jackson2- поставщик% 7C3.1.0.CR3% 7Cjar). –

+0

Хм, но можете ли вы предоставить версию jackson-core, которая разрешена вашим проектом. – Shashank

ответ

3

Этот класс будет переопределять по умолчанию Jackson2JsonProvider поможет вам настроить ObjectMapper, в соответствии с вашими потребностями, например, serializer для Path.class переопределяется ToStringSerializer из NioPathSerializer.

@Provider 
@Consumes({"application/*+json", "text/json"}) 
@Produces({"application/*+json", "text/json"}) 
public class CustomJacksonProvider extends JacksonJsonProvider { 

    public CustomJacksonProvider(){ 
    super(configureMapper(new ObjectMapper())); 
    } 

    public static ObjectMapper configureMapper(ObjectMapper mapper) { 
    SimpleModule m = new SimpleModule("PathToString"); 
    m.addSerializer(Path.class,new ToStringSerializer()); 
    mapper.registerModule(m); 
    return mapper; 
    } 

} 

Или вы можете использовать ContextResolver также для того же, который будет использовать ResteasyJackson2Provider (Resteasy по умолчанию)

@Provider 
@Consumes({"application/*+json", "text/json"}) 
@Produces({"application/*+json", "text/json"}) 
public class CustomJacksonProvider implements ContextResolver<ObjectMapper> { 

    final ObjectMapper mapper; 

    public CustomJacksonProvider(){ 
    mapper = new JsonMapperConfigurator(new ObjectMapper(), JacksonJaxbJsonProvider.DEFAULT_ANNOTATIONS) 
      .getConfiguredMapper(); 
    SimpleModule m = new SimpleModule("PathToString"); 
    m.addSerializer(Path.class, new ToStringSerializer()); 
    } 

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

Добавьте это к вам web.xml независимо от Approch вы используете:

<context-param> 
     <param-name>resteasy.providers</param-name> 
     <param-value>package.to.CustomJacksonProvider</param-value> 
    </context-param> 

Если вы не используете файл web.xml, верните класс провайдера в свой метод Application.getClasses() вашего приложения JAX-RS.

Надеюсь, это поможет.

Edited:

Кроме того, вы можете пропустить поддержку аннотаций JAXB в реализации этого вы должны настроить это вручную, так как JacksonJaxbJsonProvider не ищет распознаватель/поставщик JsonMapperConfigurator. Используйте JsonMapperConfiguration с картографом в CustomJackosnProvider вместе с JacksonJaxbJsonProvider.DEAFULT_ANNOATIONS. В идеале, поскольку нет никаких предустановок для предоставления JsonMapperConfiguration Поставщик-резольвер/поставщик и поставщик Jackson от RestEasy не предоставляют конструктор arg, поставщик лучше всего. Поскольку они приняли это решение, в идеале они не должны делать никаких настроек в ResteasyJackson2Provider. Кроме того, только функциональность в ResteasyJackson2Provider - это пользовательский тип json и лучшее кэширование картографа на уровне класса.

Обновленный код в соответствии с объяснением

+0

Итак, какой подход я использовал бы, если бы хотел, чтобы я был как можно более неинвазивным? Кажется, что в первом подходе (если я не продлеваю «ResteasyJackson2Provider» --- но я хочу сохранить RESTEasy-специфические вещи из моего кода), я выброшу все возможности, которые может предоставить ResteasyJackson2Provider. В любом случае, похоже, я создаю новую «ObjectMapper». Но код для 'ResteasyJackson2Provider', похоже, ищет существующий' ObjectMapper'. Я отбрасываю некоторые встроенные функции, создавая новый? –

+0

Woohoo --- он работает! Большое спасибо за четкое и лаконичное изложение шагов по решению этой проблемы. (Я хотел бы получить разъяснения, см. Другие комментарии.) Я отредактировал ответ, чтобы включить инструкции по регистрации класса в конфигурации, отличной от 'web.xml'. И жаль, что я так долго думал о том, что награда была автоматически присуждена на 50%. Вы дали такой полезный ответ, что я хотел начать еще одну награду, чтобы удостовериться, что вы получаете то, что заслуживаете, но это заставляет очень высокую награду за вторую. :( –

+0

Я бы порекомендовал второй, потому что он, ContextResolver предназначен для этой цели, не пропуская какой-либо другой функции, предоставляемой reasteasy. – Shashank