我正在将 REST/JSON 服务从 Coldfusion 9 转换为 Spring-MVC 3.1 应用程序。我正在使用 Jackson (1.9.5) 和 Spring 提供的 MappingJacksonJsonConverter,并且正在自定义 ObjectMapper 以使用 CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES 命名字段。
我面临的问题是我们的旧服务将“驼峰式大小写为带有下划线的大写”作为 json 属性名称。这个 JSON 的消费者(也是用 ColdFusion 编写的)可能不太关心大小写,但杰克逊确实关心大小写,并抛出 UnrecognizedPropertyExceptions。
在查看了我可以从 ObjectMapper 获得的几乎所有设置之后 - DeserializationConfig、DeserializerProvider 等,我最终得到了一个非常混乱的 hack,我在其中解析为 JSON 树,使用自定义 JsonGenerator 输出它,该 JsonGenerator 将字段名称小写,并且然后将其解析为对象。
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();
}
};
这个解决方案似乎效率很低。有一个适用于地图键的解决方案,但我无法为字段名称找到类似的解决方案。
另一种解决方案是使用两个设置器,一个使用旧字段名称进行注释。必须扩展命名策略以忽略这些字段,在我的情况下这很好,因为对象映射器不会使用 UPPER_UNDERSCORE 策略处理任何其他类:
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());
}
}
这比以前的解决方案更理想,但每个字段都需要两个带注释的设置器 - 一个用于新格式,一个用于支持旧格式。
有没有人遇到过让杰克逊对反序列化字段名称不区分大小写的方法,或者就此而言接受字段名称的多个别名?