Garret Wilson Garret Wilson - 16 days ago 4
JSON Question

Jackson 2 incorrectly serializing Java java.nio.file.Path

I'm using RESTEasy 3.1.0.CR3 with Tomcat 8.5.6 inside Eclipse 4.6.1, with the JSBoss resteasy-jackson2-provider. I have a simple JavaBean

FooBar
that returns a string "ID" and a
java.nio.file.Path
"path".

Jackson makes this easy to serialize to JSON. In my JAX-RS resource I simply specify
@Produces("application/json; charset=UTF-8")
. But Jackson is not using
Path.toString()
. Instead it appears to be using
Path.toURI().toString()
or something:

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


Why!?? And more importantly, how can I get Jackson to simply use the
toString()
version of
Path
?

Here is my project dependency tree:

+- 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


Note that it is not acceptable for me to add annotations to my
FooBar
class, which is a domain model class that should have no coupling with the RESTful API serialization details.

I want a simple way to hook into Jackson2 in RESTEasy and modify the serialization of a
Path
property value without modifying my class or writing a custom serializer for my class.

Answer

This class will override default Jackson2JsonProvider help you to configure ObjectMapper, according to you needs, like serializer for Path.class is overriden to ToStringSerializer from 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;
  }

}

Or you can use ContextResolver also for the same which will use ResteasyJackson2Provider(RestEasy default)

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

  final ObjectMapper mapper;

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

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

Add this to you web.xml irrespective of approch you use:

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

If you are not using a web.xml file, return the provider class in your JAX-RS application's Application.getClasses() method.

Hope this helps.