2012-04-09 8 views
11

में वैकल्पिक संपत्ति नामों को संभालना मैं एक आरईएसटी/जेएसओएन सेवा कोल्डफ्यूजन 9 से स्प्रिंग-एमवीसी 3.1 एप्लिकेशन में परिवर्तित करने पर काम कर रहा हूं। मैं जैक्सन (1.9.5) और मैपिंग जैक्सन जेसन कनवर्टर का उपयोग कर रहा हूं जो वसंत प्रदान करता है, और ऑब्जेक्टमैपर को CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES के साथ फ़ील्ड नाम देने के लिए अनुकूलित कर रहा हूं।जैक्सन Deserialization

मुझे जिस समस्या का सामना करना पड़ रहा है वह यह है कि हमारी विरासत सेवा जेसन संपत्ति के नाम के रूप में 'अंडरस्कोर के साथ ऊपरी मामले में ऊंट केस' उत्पन्न करती है। इस जेएसओएन के उपभोक्ता, कोल्डफ्यूजन में भी लिखे गए हैं, मामले के बारे में कम ख्याल रख सकते हैं, लेकिन जैक्सन मामले की परवाह करता है, और अपरिचित प्रॉपर्टी अपवादों को फेंक देता है।

लगभग हर मैं 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 है जो मानचित्र की कुंजी के लिए काम करता है, लेकिन मैं फ़ील्ड नामों के लिए एक समान समाधान नहीं ढूंढ पाया।

एक वैकल्पिक समाधान दो सेटर्स होना है, जो विरासत क्षेत्र के नाम से एनोटेटेड है। नामकरण रणनीति जो मेरी स्थिति में ठीक है के बाद से वस्तु नक्शाकार एक 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

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

सुझाव के लिए धन्यवाद मैं यह कोशिश की है, लेकिन यह समस्या ठीक नहीं होती समस्या लगती है कि नामकरण रणनीति। एक तरीका है। कुछ क्षेत्रों, सेटर्स, गेटर्स और कन्स्ट्रक्टर के साथ पीओजेओ को दिया जा सकता है उन पर टिप्पणियां, उन्हें जेसन फील्ड नामों में परिवर्तित करें। एक फ़ील्ड/विधि/कन्स्ट्रक्टर केवल एक जेसन फ़ील्ड नाम पर मैप कर सकता है। –

+0

इसके अलावा, PropertyNamingStrategy किसी भी टिप्पणी के बाद लागू किया जाता है।तो अगर मैं दो सेटर्स, एक @ जेसनप्रोपर्टी ("test_field") और अन्य @ जेसनप्रोपर्टी ("TEST_FIELD") को एनोटेट करना था, तो निम्न केस रूपांतरण इन दो सेटर्स को टकराएगा, जो अपवाद फेंकता है। –

5

आप जैक्सन 1.x और न जैक्सन 2.x उपयोग कर रहे हैं के बाद से, आप mixins इस्तेमाल कर सकते हैं। जो मैं समझता हूं उससे, आप UPPER_CASE को समझने के लिए deserialization चाहते हैं, लेकिन आप low_case में serialize करना चाहते हैं। एक Mixin जैसे, क्रमबद्धता config में अक्रमांकन config में जोड़े लेकिन नहीं:

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 के साथ: mixins क्रमबद्धता और अक्रमांकन config द्वारा साझा कर रहे हैं। मैं अभी तक जैक्सन 2.x :(

1

स्प्रिंग बूट का उपयोग करते समय यह भी Jackson2ObjectMapperBuilderCustomizer उपयोग करना संभव है । जो डिफ़ॉल्ट ऑटो विन्यास को बरकरार रखे हुए यह एक सेम जोड़कर किया जा सकता है:।

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