Reputation: 10400
(Jackson 2.6.1, Jersey 2.21.0)
My webapp uses JAX-RS interfaces and Jackson json helper classes. I do not directly import Jersey classes, plan on keeping it irrelevant to my app.
I want to reuse ObjectMapper and JsonFactory instances as Jackson docs recommend, but unable to get reference to them inside MyBeanService class.
// resolver returns null
ContextResolver<ObjectMapper> resolver = providers.getContextResolver(ObjectMapper.class, MediaType.APPLICATION_JSON_TYPE);
ContextResolver<JacksonJaxbJsonProvider> resolverB = providers.getContextResolver(JacksonJaxbJsonProvider.class, MediaType.WILDCARD_TYPE);
// returns org.glassfish.jersey.server.ResourceConfig$WrappingResourceConfig wrapper
// also I wish not to import directly any jersey classes
ContextResolver<Application> resolverC = providers.getContextResolver(Application.class, MediaType.WILDCARD_TYPE);
// resolver was null so getters don't work
ObjectMapper mapper = resolver.getContext(ObjectMapper.class);
JsonGenerator jsonG = mapper.getFactory().createGenerator(os, JsonEncoding.UTF8);
Should I just provide public static ObjectMapper MyApplication.getObjectMapper()
function? MyApplication would set private static objectmapper field in getSingletons() method?
This is my configuration and classes to provide restjson servlet path.
mywebapp/WEB-INF/web.xml
Application instance given in web.xml file.
...
<servlet><servlet-name>myapp.rest.MyApplication</servlet-name></servlet>
<servlet-mapping>
<servlet-name>myapp.rest.MyApplication</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
MyApplication.java
Set rest services and configure objectmapper instance such as datetime field format.
package myapp.rest;
import java.util.*;
import java.text.*;
import javax.ws.rs.core.Application;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
import com.fasterxml.jackson.jaxrs.cfg.Annotations;
public class MyApplication extends Application {
@Override public Set<Class<?>> getClasses() {
Set<Class<?>> list = new HashSet<Class<?>>();
list.add(MyBeanService.class);
return list;
}
@Override public Set<Object> getSingletons() {
Set<Object> list = new HashSet<Object>();
ObjectMapper mapper = new ObjectMapper();
mapper.disable(SerializationFeature.INDENT_OUTPUT);
//mapper.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); // enable=1433435279692 (utcMillis)
//mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); // disable=2015-06-04T16:25:27.056+0000 (ISO8601_utc)
DateFormat dtf = SimpleDateFormat.getDateTimeInstance();
((SimpleDateFormat)dtf).applyPattern("yyyy-MM-dd'T'HH:mm:ssZ"); // 2015-06-04T19:25:27+0300 (custom+tzOffset)
mapper.setDateFormat(dtf);
JacksonJaxbJsonProvider provider = new JacksonJaxbJsonProvider(
mapper,
new Annotations[] { Annotations.JACKSON, Annotations.JAXB } );
list.add(provider);
return list;
}
}
MyBeanService.java
This is a service class handling rest url paths.
import java.io.*;
import java.util.*;
import javax.inject.Singleton;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.core.StreamingOutput;
import javax.ws.rs.core.*;
import javax.ws.rs.ext.*;
@Path("") @Singleton
public class MyBeanService {
//@Context private Providers providers;
//@Context private Application application;
@GET @Path("/{serverName}/beans/{id}")
@Produces({"application/json;charset=UTF-8"})
public Response getBean (
@Context HttpServletRequest req,
@PathParam("serverName") String serverName,
@PathParam("id") long id) {
final MyServer server= JPADB.getServer(serverName);
final MyBean bean = JPADB.getBean(server, id);
StreamingOutput stream = new StreamingOutput() {
@Override public void write(OutputStream os) throws IOException, WebApplicationException {
//*** I want to reuse JsonFactory and ObjectMapper, but
// currently create new factory each time
JsonFactory jsonF = new JsonFactory();
JsonGenerator jsonG = jsonF.createGenerator(os, JsonEncoding.UTF8);
try {
jsonG.writeStartObject();
jsonG.writeNumberField("id", bean.getId());
jsonG.writeStringField("title", bean.getTitle());
jsonG.writeStringField("serverId", server.getId());
jsonG.writeStringField("serverName", server.getName());
..various json fields, some optional, some at runtime autogen..
jsonG.writeEndObject();
jsonG.flush();
} finally {
jsonG.close();
}
}
};
CacheControl cc = new CacheControl();
cc.setNoCache(true);
return Response.ok().type("application/json;charset=UTF-8")
.cacheControl(cc).entity(stream).build();
}
}
Upvotes: 2
Views: 4031
Reputation: 209022
The ContextResolver
is null because you don't actually have a ContextResolver
. This is something you need to write yourself.
@Provider
@Produces("application/json")
@Consumes("application/json")
public class MapperContextResolver implements ContextResolver<ObjectMapper> {
private final ObjectMapper mapper;
public MapperContextResolver() {
mapper = new ObjectMapper();
// do any configurations to mapper
}
@Override
public ObjectMapper getContext(Class<?> cls) {
return mapper;
}
}
Then just register it with the application. How the Jackson(Jaxb)JsonProvider
works is that first it looks for an ObjectMapper
that was passed to the constructor. If not found, then it looks for a ContextResolver
for the ObjectMapper
. If not found, it will just use its own ObjectWriter
and ObjectReader
.
So you don't need to create the JacksonJaxbJsonProvider
with the ObjectMapper
. Just use the default constructor, and the provider will look for your resolver.
Upvotes: 4