处理杰克逊反序列化中的备用属性名称

Dan*_*att 11 java json jackson

我正在努力将REST/JSON服务从Coldfusion 9转换为Spring-MVC 3.1应用程序.我使用的杰克逊(1.9.5)和MappingJacksonJsonConverter Spring提供,和我定制ObjectMapper与CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES名称字段.

我面临的问题是我们的遗留服务产生"带有下划线的UPPER案例的驼峰案例"作为json属性名称.这个JSON的消费者,也是用ColdFusion编写的,可能不关心案例,但Jackson确实关心案例,并抛出UnrecognizedPropertyExceptions.

寻找到几乎所有设置后,我可以从ObjectMapper达到 - DeserializationConfig,DeserializerProvider,等我结束了一个非常混乱的黑客中,我解析到一个JSON树,自定义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();
    }
};
Run Code Online (Sandbox Code Playgroud)

这个解决方案似乎非常低效.有一个解决方案适用于地图的键,但我无法找到类似的字段名称解决方案.

另一种解决方案是拥有两个setter,一个用传统字段名称注释.命名策略必须扩展为忽略这些字段,在我的情况下这很好,因为对象映射器不会处理任何其他具有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());
    }
}
Run Code Online (Sandbox Code Playgroud)

这比以前的解决方案更理想,但每个字段需要两个带注释的setter - 一个用于新格式,一个用于支持传统格式.

有没有人遇到过让杰克逊对字段名称进行反序列化不敏感的方法,或者对于字段名称接受多个别名?

Dan*_*att 12

这已经在Jackson 2.5.0中得到修复.

ObjectMapper mapper = new ObjectMapper();
mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
Run Code Online (Sandbox Code Playgroud)

  • `mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);` 也应该有效。 (2认同)

Eug*_*sky 6

您可以使用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());
Run Code Online (Sandbox Code Playgroud)


Pas*_*nas 5

由于您使用的是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());
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,你不能用Jackson 2.x做到这一点:mixIns由序列化和反序列化配置共享.我还没有找到一种方法来处理Jackson 2.x的别名:(