繁体   English   中英

在 Jackson 反序列化中处理备用属性名称

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM