2012-04-09 56 views
11

我正在将ColdFusion 9中的REST/JSON服务转换为Spring-MVC 3.1应用程序。我使用了Spring提供的Jackson(1.9.5)和MappingJacksonJsonConverter,并使用CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES自定义ObjectMapper以命名字段。在Jackson反序列化中处理备用属性名称

我面临的问题是,我们的遗留服务产生'骆驼案件与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(); 
    } 
}; 

该解决方案似乎效率很低。有一个solution适用于地图的键,但我无法找到字段名称的类似解决方案。

另一种解决方案是使用两个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()); 
    } 
} 

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

有没有人遇到过让杰克逊对反序列化字段名称不区分大小写的方法,或者为此接受字段名的多个别名?

回答

12

这已被固定为杰克逊2.5.0的别名。

ObjectMapper mapper = new ObjectMapper(); 
mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true); 
+1

'mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);'也应该有效。 – Daniel 2017-02-20 21:54:19

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()); 
+0

感谢您的建议我已经尝试过了,但它并没有解决问题的问题似乎是,在命名策略。是一种方式,给定一个POJO,包含一些字段,setter,getters和构造函数将它们转换为json字段名称。一个字段/方法/构造函数只能映射到一个json字段名称。 – 2012-04-10 01:46:08

+0

另外,PropertyNamingStrategy在任何注释之后应用。因此,如果我要注释两个setter,一个@JsonProperty(“test_field”)和另一个@JsonProperty(“TEST_FIELD”),则小写转换会导致这两个setter发生冲突,从而引发异常。 – 2012-04-10 01:51:31

5

由于您使用的杰克逊1.x和不杰克逊2.x中,你可以使用混入。根据我的理解,你想要反序列化来理解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()); 
    } 
} 

但是,你不能做到这一点与杰克逊2.X:混入被序列化和反序列化配置共享。我还没有找到一种方法来处理与杰克逊2.X :(

1

当使用Spring引导它也可以使用Jackson2ObjectMapperBuilderCustomizer 。它保留默认的自动配置这可以通过添加一个bean来完成:

@Bean 
public Jackson2ObjectMapperBuilderCustomizer acceptCaseInsensitive() { 
    return builder -> builder.featuresToEnable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES); 
} 
相关问题