I'm using a customised ContextResolver with my JAX-RS application.
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#Provider
public class ObjectMapperContextResolver
implements ContextResolver<ObjectMapper> {
public ObjectMapperContextResolver() {
super();
// I want to control this
objectMapper = new ObjectMapper()
.configure(MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME, true);
objectMapper.registerModule(new JaxbAnnotationModule());
}
#Override
public ObjectMapper getContext(final Class<?> type) {
return objectMapper;
}
private final ObjectMapper objectMapper;
}
As you can see, there is an option(MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME) customised.
How can I turn off that option on demand in JAX-RS resources?
#Path("/items")
public class ItemsResource {
#GET
#Produces({APPLICATION_JSON, APPLICATION_XML})
public Response read(#QueryParam("wrap") final boolean wrap) {
if (wrap) {
// turn off MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME
// and then return response of [Items]
} else {
// turn on MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME
// and then return response of [List<Item>]
}
}
}
Is there any way to be injected with the provider?
Related
Is it possible somehow to reuse Jackson ObjectMapper reference in my test classes when I testing my rest services using Jersey test framework.
I registered JacksonFeature in my abstract class from which my test classes extends.
public abstract class AbstractRestIntegrationTest extends JerseyTest {
#Override
protected Application configure() {
ResourceConfig resourceConfig = new ResourceConfig(getResourceClass());
resourceConfig.register(JacksonFeature.class);
resourceConfig.register(MultiPartFeature.class);
return resourceConfig;
}
}
EDIT
In my test class I calling the rest service, which returns to me json. Then I want to parse this json by jackson and check if it contains objects. And my question is if I can reuse that ObjectMapper in test method from Jersey when I register JacksonFeature.
public class ManagedElementIntegrationTest extends AbstractRestIntegrationTest {
#Override
protected Class<?> getResourceClass() {
return ManagedElementRestService.class;
}
#Test
public void testGetManagedElementById() throws IOException {
IManagedElementService managedElementService = getBeanOfClass(IManagedElementService.class);
ManagedElement me = prepareManagedElementObject();
try {
when(managedElementService.getUpdatedApplication(anyString())).thenReturn(me);
} catch (ManagedElementNotFoundException e) {
fail("Exception not expected: " + e);
}
String response = target("me/app").request().accept(MediaType.APPLICATION_JSON).get(String.class);
ObjectMapper mapper = new ObjectMapper();
JsonNode actualObj = mapper.readTree(response);
assertNotNull(actualObj);
assertNotNull(actualObj.get("mapPosition"));
assertNull(actualObj.get("alarms"));
// code continues below.
}
}
I'm trying to figure how to explain Jersey and Jackson how to deserialize a Future that I pass as byte[].
I create my own ContextResolver
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
private static ObjectMapper mapper = null;
public ObjectMapperContextResolver() {
mapper = ObjectMapperFactory.getObjectMapper();
}
#Override
public ObjectMapper getContext(Class<?> type) {
return mapper;
}
}
And the implementation of the ObjectMapper
public static ObjectMapper getObjectMapper() {
ObjectMapper defaultObjectMapper = new ObjectMapper();
SimpleModule futureModule = new SimpleModule("FutureModule");
futureModule.<Future>addDeserializer(Future.class, new FutureDeserializer<String>());
defaultObjectMapper.registerModule(futureModule);
return defaultObjectMapper;
}
And then finally in the implementation of my FutureDeserializer
public class FutureDeserializer<T> extends StdDeserializer<Future<T>>{
public FutureDeserializer() {
super(Future.class);
}
#Override
public Future<T> deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException {
ObjectMapper mapper=(ObjectMapper)jp.getCodec();
//TODO: Breakpoint never stop here
return null;
}
}
Then I register in my ResourceConfig before start the JerseyTest
ResourceConfig rc = new ResourceConfig();
rc.register(SpringLifecycleListener.class);
rc.register(RequestContextFilter.class);
rc.register(new JacksonFeature());
rc.register(new ObjectMapperContextResolver());
But when I run the test the ObjectMapperContextResolver is invoked and the mapper returned to Jersey, but he never use the FutureDeserializer.
Any idea what I´m doing wrong?
this is my Endpoint in which I would like to add a "Firma" with a post request, but the JSON cannot implicit parse the timestamp.
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response addFirma(Firma firma){
firmaFacade.create(firma);
return Response.ok().build();
}
Those are the variables of "Firma"
private int firm1;
private LocalDate firm2;
And this is the JSON-String I sent - LocalDate is NULL
{
"firm1":2,
"firm2":"2017-09-09"
}
But whenever I use the get Request with test data, it will show me the right result:
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response findFirma() {
List<Firma> list = firmaFacade.findAll();
GenericEntity<List<Firma>> result = new GenericEntity<List<Firma>>(list) { };
return Response.ok().entity(result).build();
}
Please help someone.
If you are using Jackson, add the following dependency to your project:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.version}</version>
</dependency>
Then create a ContextResolver for ObjectMapper and register the JavaTimeModule:
#Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
private final ObjectMapper mapper;
public ObjectMapperContextResolver() {
this.mapper = createObjectMapper();
}
#Override
public ObjectMapper getContext(Class<?> type) {
return mapper;
}
private ObjectMapper createObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
return mapper;
}
}
Currently facing an issue where I would like to alter the response of jax-rs resources based on some information passed into the response. The change that need to be made is the format of some of the json. Currently I am registering an ObjectMapper using jax #Provider and ContextResolver. However the getContext() method is only invoked a single time for each resource class. It is not possible to alter this based on every request.
Is it possible to inject or access the ObjectMapper in a ContainerResponseFilter?
#Provider
public class ObjectMapperResolver implements ContextResolver<ObjectMapper> {
#Context
private HttpServletRequest httpRequest;
private final ObjectMapper mapper;
public ObjectMapperContextResolver() {
mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Date.class, new DateDeserializer("some data format"));
mapper.registerModule(module);
}
#Override
public ObjectMapper getContext(Class<?> type) {
return getMapperBasedOnRequest();
}
private ObjectMapper getMapperBasedOnRequest() {
if (true) {
return mapper;
} else {
return //something else;
}
}
private boolean containsLegacyHeader() {
return //get some information from the request headers or body
}
}
I decided to solve this issue by using a Filter which would be invoked based on a parameter provided in the request, in this case a header.
public class DemoFilter implements ContainerResponseFilter {
private final String UTC = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
private final String OTHER = "yyyy-MM-dd";
public static final String DATE_UTC = "utc";
private String getMapperBasedOnRequest(ContainerRequestContext requestContext) {
if (checkHeader(requestContext)) {
return OTHER;
} else {
return UTC;
}
}
private boolean checkHeader(ContainerRequestContext requestContext) {
return //check header
}
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
ObjectWriterInjector.set(new DateMod(getMapperBasedOnRequest(requestContext)));
}
public static class DateMod extends ObjectWriterModifier {
private String df;
public DateMod(String df) {
this.df = df;
}
#Override
public ObjectWriter modify(EndpointConfigBase<?> endpointConfigBase, MultivaluedMap<String, Object> multivaluedMap, Object o, ObjectWriter objectWriter, JsonGenerator jsonGenerator) throws IOException {
return objectWriter.with(new SimpleDateFormat(dateFormat));
}
}
}
I have a jersey2 application configured for JSON support via Jackson, adding
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey.version}</version>
</dependency>
in the POM file and
public MyApplication() {
...
register(JacksonFeature.class)
...
}
in my application. Everything works, my resources get deserialized POJOs as arguments
#POST #Consumes(MediaType.APPLICATION_JSON)
public void blah(MyPojo p) {
...
}
Now one of thoese resources needs a reference to Jackson's ObjectMapper to do some deserialization on its own. I've tried doing something like
#Inject
public MyResource(#Context ObjectMapper mapper) {
...
}
or
#GET
public String foo(#Context ObjectMapper mapper) {
...
}
but in both cases the reference to mapper is null. How can I inject a reference to the ObjectMapper in my resources?
First there is no default ObjectMapper used by the Jackson provider. It doesn't use an ObjectMapper at all actually. It makes use of other Jackson APIs to handle the (de)serialization.
If you want to use/inject a single ObjectMapper instance, then you should just create a Factory for it
public class ObjectMapperFactory implements Factory<ObjectMapper> {
final ObjectMapper mapper = new ObjectMapper();
#Override
public ObjectMapper provide() {
return mapper;
}
#Override
public void dispose(ObjectMapper t) {}
}
Then bind it
register(new AbstractBinder(){
#Override
public void configure() {
bindFactory(ObjectMapperFactory.class)
.to(ObjectMapper.class).in(Singleton.class);
}
});
One thing should be noted is that any configuration of the ObjectMapper is not thread safe. So say you tried to configure it from your resource method, those operations are not thread safe.
Another thing to note with the Jackson provider, is that if we provide a ContextResolver, like mentioned by #Laurentiu L, then the Jackson provider will switch to using our ObjectMapper. In which case, if you want to use that same ObjectMapper, then you can look it up in the Factory. For example
public class ObjectMapperFactory implements Factory<ObjectMapper> {
private final Providers providers;
final ObjectMapper mapper = new ObjectMapper();
public ObjectMapperFactory(#Context Providers providers) {
this.providers = providers;
}
#Override
public ObjectMapper provide() {
ContextResolver<ObjectMapper> resolver = providers.getContextResolver(
ObjectMapper.class, MediaType.APPLICATION_JSON);
if (resolver == null) { return mapper; }
return resolver.getContext(null);
}
#Override
public void dispose(ObjectMapper t) {}
}
For the above to work (use a single ObjectMapper), you need to make sure to implement the ContextResolver<ObjectMapper>, and make sure to annotation the ContextResolver with the corresponding #Produces and #Consumes media types.
Aside from the JacksonFeature you need to register a ContextResolver for ObjectMapper.
Simple example from the Documentation at 9.1.4.2. Configure and register
#Provider
public class MyObjectMapperProvider implements ContextResolver<ObjectMapper> {
final ObjectMapper defaultObjectMapper;
public MyObjectMapperProvider() {
defaultObjectMapper = createDefaultMapper();
}
#Override
public ObjectMapper getContext(Class<?> type) {
return defaultObjectMapper;
}
private static ObjectMapper createDefaultMapper() {
final ObjectMapper result = new ObjectMapper();
result.configure(Feature.INDENT_OUTPUT, true);
return result;
}
// ...
}
Complete code example
available on Github
You will also need to register it
.register(MyObjectMapperProvider.class)