2012-04-09 27 views
11

Tôi đang làm việc để chuyển đổi dịch vụ REST/JSON từ Coldfusion 9 sang ứng dụng Spring-MVC 3.1. Tôi đang sử dụng Jackson (1.9.5) và MappingJacksonJsonConverter mà Spring cung cấp, và đang tùy chỉnh ObjectMapper để đặt tên các trường bằng CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES.Xử lý các tên thuộc tính thay thế trong Jackson Deserialization

Sự cố tôi đang gặp phải là dịch vụ kế thừa của chúng tôi tạo 'trường hợp lạc đà tới trường hợp UPPER có dấu gạch dưới' làm tên thuộc tính json. Người tiêu dùng của JSON này, cũng được viết bằng ColdFusion, có thể quan tâm ít hơn về trường hợp, nhưng Jackson quan tâm đến trường hợp và ném UnrecognizedPropertyExceptions.

Sau khi xem xét mọi thiết lập mà tôi có thể tiếp cận từ ObjectMapper - DeserializationConfig, DeserializerProvider, v.v., tôi đã kết thúc với một hack rất lộn xộn, trong đó tôi phân tích cú pháp thành cây JSON, xuất nó với một JsonGenerator tùy chỉnh tên trường, và sau đó phân tích cú pháp nó trở thành một đối tượng.

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(); 
    } 
}; 

Giải pháp này có vẻ rất không hiệu quả. Có solution hoạt động cho các khóa của bản đồ, nhưng tôi không thể tìm thấy giải pháp tương tự cho tên trường.

Giải pháp thay thế là có hai bộ định vị, một giải pháp được chú thích với tên trường cũ. Chiến lược đặt tên đã được mở rộng để bỏ qua các lĩnh vực này, mà trong hoàn cảnh của tôi là tốt vì đối tượng mapper sẽ không được đối phó với bất kỳ lớp học khác với một chiến lược 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()); 
    } 
} 

Đây là chi tiết lý tưởng hơn so với trước đây giải pháp, nhưng sẽ yêu cầu hai bộ giải mã có chú thích cho mọi trường - một cho định dạng mới, một để hỗ trợ định dạng cũ.

Có ai đi qua một cách để làm cho Jackson không phân biệt chữ hoa chữ thường hay không, hoặc cho vấn đề đó chấp nhận nhiều bí danh cho một tên trường?

Trả lời

12

này đã được khắc phục của Jackson 2.5.0.

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

'mapper.enable (MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);' cũng sẽ hoạt động. – Daniel

6

Bạn có thể sử dụng 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

Cảm ơn bạn đã đề xuất, nhưng nó không khắc phục được vấn đề. Với một số POJO với một số trường, setters, getters và constructors có thể có chú thích trên chúng, chuyển đổi chúng thành tên trường json. Một trường/phương thức/hàm tạo chỉ có thể ánh xạ tới một tên trường json. –

+0

Ngoài ra, PropertyNamingStrategy được áp dụng sau bất kỳ chú thích nào.Vì vậy, nếu tôi chú thích hai bộ định cư, một @JsonProperty ("test_field") và một @JsonProperty khác ("TEST_FIELD"), chuyển đổi trường hợp thấp hơn sẽ làm cho hai bộ định cư này va chạm, điều này sẽ loại trừ một ngoại lệ. –

5

Vì bạn đang sử dụng Jackson 1.x và không Jackson 2.x, bạn có thể sử dụng mixins. Từ những gì tôi hiểu, bạn muốn deserialization để hiểu UPPER_CASE nhưng bạn muốn serialize trong lower_case. Thêm một mixin trong cấu hình deserialization nhưng không phải trong cấu hình serialization, như vậy:

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()); 
    } 
} 

Tuy nhiên, bạn không thể làm điều đó với Jackson 2.x: mixins được chia sẻ bởi các serialization và deserialization config. Tôi vẫn chưa tìm ra cách để xử lý các bí danh với Jackson 2.x :(

1

Khi sử dụng Spring Boot nó cũng có thể sử dụng Jackson2ObjectMapperBuilderCustomizer Điều này có thể được thực hiện bằng cách thêm một bean:

@Bean 
public Jackson2ObjectMapperBuilderCustomizer acceptCaseInsensitive() { 
    return builder -> builder.featuresToEnable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES); 
} 
Các vấn đề liên quan