![](/img/trans.png)
[英]Jackson mapping: Deserialization of JSON with different property names
[英]Handling alternate property names in Jackson Deserialization
我正在将 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 中,可能不太关心大小写,但 Jackson 确实关心大小写,并抛出 UnrecognizedPropertyExceptions。
在查看了我可以从 ObjectMapper - DeserializationConfig、DeserializerProvider 等获得的几乎所有设置之后,我最终得到了一个非常混乱的 hack,我在其中解析了一个 JSON 树,output 它使用自定义 JsonGenerator 将字段名称小写,并且然后将其解析为 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();
}
};
该解决方案似乎效率很低。 有一个解决方案适用于 map 的键,但我无法为字段名称找到类似的解决方案。
另一种解决方案是使用两个设置器,一个用遗留字段名称注释。 必须扩展命名策略以忽略这些字段,在我的情况下这很好,因为 object 映射器不会使用 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());
}
}
这比以前的解决方案更理想,但每个字段都需要两个带注释的设置器——一个用于新格式,一个用于支持旧格式。
有没有人遇到过使 Jackson 对反序列化字段名称不区分大小写,或者就此接受字段名称的多个别名的方法?
这已从 Jackson 2.5.0 开始修复。
ObjectMapper mapper = new ObjectMapper();
mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
您可以使用 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());
由于您使用的是 Jackson 1.x 而不是 Jackson 2.x,因此您可以使用 MixIns。 据我了解,您希望反序列化以了解 UPPER_CASE,但您希望以 lower_case 进行序列化。 在反序列化配置中添加 MixIn 但不在序列化配置中,如下所示:
public class JsonNamingTest {
public static class A {
private String testField;
public String getTestField() {
return testField;
}
public void setTestField(String field) {
this.testField = field;
}
}
public static class MixInA {
@JsonProperty("TEST_FIELD")
public void setTestField(String field) {}
}
@Test
public void something() throws Exception {
A test = new A();
test.setTestField("test");
ObjectMapper mapper = new ObjectMapper();
mapper.getDeserializationConfig().addMixInAnnotations(A.class, MixInA.class);
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());
}
}
但是,您不能使用 Jackson 2.x 执行此操作:mixIns 由序列化和反序列化配置共享。 我还没有找到一种方法来处理 Jackson 2.x 的别名:(
使用 Spring Boot 时,也可以使用保留默认自动配置的Jackson2ObjectMapperBuilderCustomizer 。 这可以通过添加一个 bean 来完成:
@Bean
public Jackson2ObjectMapperBuilderCustomizer acceptCaseInsensitive() {
return builder -> builder.featuresToEnable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.