I am working on converting a REST/JSON service from Coldfusion 9 to a Spring-MVC 3.1 application. I am using Jackson (1.9.5) and the MappingJacksonJsonConverter that Spring provides, and am customizing the ObjectMapper to name fields with CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES.
The problem I am facing is that our legacy service produces 'camel case to UPPER case with underscores' as json property names. The consumers of this JSON, also written in ColdFusion, could care less about case, but Jackson does care about case, and throws UnrecognizedPropertyExceptions.
After looking into just about every setting I could reach from ObjectMapper - DeserializationConfig, DeserializerProvider, etc, I ended up with a very messy hack in which I parse to a JSON tree, output it with a custom JsonGenerator that lower cases the field names, and then parses it back in as an object.
MappingJacksonHttpMessageConverter mc = new MappingJacksonHttpMessageConverter() {
@Override
protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return this.getObjectMapper().readValue(translateToLowerCaseKeys(inputMessage.getBody()), getJavaType(clazz));
}
private byte[] translateToLowerCaseKeys(InputStream messageBody) throws IOException {
StringWriter sw = new StringWriter();
JsonGenerator lowerCaseFieldNameGenerator = new JsonGeneratorDelegate(this.getObjectMapper().getJsonFactory().createJsonGenerator(sw)) {
@Override
public void writeFieldName(String name) throws IOException, org.codehaus.jackson.JsonGenerationException {
delegate.writeFieldName(name.toLowerCase());
};
};
this.getObjectMapper().writeTree(lowerCaseFieldNameGenerator, this.getObjectMapper().readTree(messageBody));
lowerCaseFieldNameGenerator.close();
return sw.getBuffer().toString().getBytes();
}
};
This solution seems very inefficient. There is a solution that works for keys of a map, but I was unable to find a similar solution for field names.
An alternative solution is to have two setters, one annotated with the legacy field name. The naming strategy has to be extended to ignore these fields, which in my situation is fine since the object mapper won't be dealing with any other classes with an UPPER_UNDERSCORE strategy:
public class JsonNamingTest {
public static class CaseInsensitive extends LowerCaseWithUnderscoresStrategy {
public String translate(String in) {
return (in.toUpperCase().equals(in) ? in : super.translate(in));
}
}
public static class A {
private String testField;
public String getTestField() {
return testField;
}
public void setTestField(String field) {
this.testField = field;
}
@JsonProperty("TEST_FIELD")
public void setFieldAlternate(String field) {
this.testField = field;
}
}
@Test
public void something() throws Exception {
A test = new A();
test.setTestField("test");
ObjectMapper mapper = new ObjectMapper().setPropertyNamingStrategy(new CaseInsensitive());
assertEquals("{\"test_field\":\"test\"}", mapper.writeValueAsString(test));
assertEquals("test", mapper.readValue("{\"test_field\":\"test\"}", A.class).getTestField());
assertEquals("test", mapper.readValue("{\"TEST_FIELD\":\"test\"}", A.class).getTestField());
}
}
This is more ideal than the previous solution, but would require two annotated setters for every field - one for the new format, one to support the legacy format.
Has anyone come across a way to make Jackson case-insensitive for deserializing field names, or for that matter accept multiple aliases for a field name?
ObjectMapper; ObjectMapper objectMapper = new ObjectMapper(); objectMapper. configure(DeserializationFeature. FAIL_ON_UNKNOWN_PROPERTIES, false); This will now ignore unknown properties for any JSON it's going to parse, You should only use this option if you can't annotate a class with @JsonIgnoreProperties annotation.
@JsonProperty can change the visibility of logical property using its access element during serialization and deserialization of JSON. @JsonAlias defines one or more alternative names for a property to be accepted during deserialization.
@JsonProperty is used to mark non-standard getter/setter method to be used with respect to json property.
To ignore individual properties, use the [JsonIgnore] attribute. You can specify conditional exclusion by setting the [JsonIgnore] attribute's Condition property. The JsonIgnoreCondition enum provides the following options: Always - The property is always ignored.
This has been fixed as of Jackson 2.5.0.
ObjectMapper mapper = new ObjectMapper();
mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
You can use PropertyNamingStrategy:
class CaseInsensitiveNaming extends PropertyNamingStrategy {
@Override
public String nameForGetterMethod(MapperConfig<?> config,
AnnotatedMethod method, String defaultName)
{
// case-insensitive, underscores etc. mapping to property names
// so you need to implement the convert method in way you need.
return convert(defaultName);
}
}
objectMapper.setPropertyNamingStrategy(new CaseInsensitiveNaming());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With