I'm using Jackson 2.7.5 with Jersey 2.25.1. I'm trying to fix existing production code that is now failing with "UnrecognizedPropertyException: Unrecognized field" when it gets an unexpected field in the JSON input.
In researching this, I found several old posts (5+ years) suggesting various fixes that were very different from my current code. I didn't pay much attention to these, because they were for old versions of Jackson/Jersey. And more recent suggestions, including Jersey's own documentation (https://jersey.github.io/documentation/latest/media.html#json.jackson), look very similar to what I already have in place. In fact, to my eyes, it looks like my existing code is already following the current practice. However, Jersey seems to be ignoring my custom ObjectMapper setting of...
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false
I'm pretty sure that setting should fix the error, but Jersey seems to be using a default ObjectMapper instead of my custom settings.
First, here is the dependency information, which I believe matches what is shown in the Jersey documentation (https://jersey.github.io/documentation/latest/media.html#json.jackson).
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey.version}</version>
</dependency>
Here is the call that is returning the error:
// this will throw an exception if it can't convert the string to the class
PropSearchResponse propResponse = null;
try {
propResponse = getResponse.readEntity(PropSearchResponse.class);
} catch(final ProcessingException e) {
throw new ProcessResultException(Code.FAILED, "failed to map from prop response", e);
}
Here is the original code for my custom ObjectMapper:
@Provider()
@Produces(value = MediaType.APPLICATION_JSON)
public class OutMapperProvider implements ContextResolver<ObjectMapper> {
private final ObjectMapper mapper;
public OutMapperProvider() {
mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, true);
mapper.setSerializationInclusion(Include.NON_NULL);
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"));
}
@Override()
public ObjectMapper getContext(final Class<?> type) {
return mapper;
}
}
Here is the example from the Jersey documentation:
@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;
}
}
I have tried the Jersey example (changing the names to match mine, of course) as well as several other examples I found online. The Jersey example does the customization after the @Override. Most other examples do it before the @Override, but they all seem substantially similar to each other and to my existing code. But it doesn't seem to make any difference. No matter what I have tried, the custom configuration is ignored and Jersey calls a default ObjectMapper, which fails on unexpected JSON fields.
Disclaimer: This is my first experience with both Jersey and Jackson. I don't have a good understanding of the underlying mechanism yet. I'm just trying to follow the patterns of the examples.
Update: I believe the code above is basically correct. But Paul's comment below says that I need to register the custom ObjectMapper. I have tried reproducing several examples I have found on the web (Example 4.2 at https://docs.huihoo.com/jersey/2.13/deployment.html#environmenmt.appmodel, for example), but without success. For my current attempt, I have tried adding a new MyApplication class to an existing config package (com.dmx.repl.config) using Jersey's ResourceConfig. The code is below. But still, it is not working.
Edit: Ignore this code, it didn't work. See solution below.
package com.dmx.repl.config;
import org.glassfish.jersey.server.ResourceConfig;
import com.dmx.repl.commons.OutMapperProvider;
/**
*
* @author Greg
* @version 1.0
*/
// Attempt to register custom ObjectMapper
public class MyApplication extends ResourceConfig {
public MyApplication() {
// I've tried both of these.
//register(OutMapperProvider.class);
packages("com.dmx.repl.commons");
}
}
It's working now. Jersey is now recognizing the custom ObjectMapper, which is configured to ignore unknown JSON fields with "FAIL_ON_UNKNOWN_PROPERTIES, false".
The ObjectMapper code above is correct. The problem (as suggested by Paul in the comments) was that the client had not registered the custom ObjectMapper. This was fixed very simply, by adding the following line to the client setup method, following client setup with ClientBuilder.
this.client.register(OutMapperProvider.class);