2015-11-13 58 views
9

我一直在嘗試升級JSON模塊以使用Jackson的FasterXML(2.6.3)版本而不是舊的Codehaus模塊。在升級過程中,我注意到使用FasterXML代替Codehaus時,命名策略有所不同。如何在使用Jackson進行反序列化時放寬命名策略?

Codehaus在命名策略方面更加靈活。以下測試突出顯示了我在使用FasterXML時遇到的問題。我如何配置ObjectMapper,使其遵循像Codehaus一樣的策略?

我無法更改JSONProperty註釋,因爲它們有數百個。我希望升級能夠在命名策略方面向後兼容。

import java.io.IOException; 

import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 
import com.fasterxml.jackson.annotation.JsonProperty; 
import com.fasterxml.jackson.databind.ObjectMapper; 
import com.fasterxml.jackson.databind.PropertyNamingStrategy; 
/*import org.codehaus.jackson.annotate.JsonIgnoreProperties; 
import org.codehaus.jackson.annotate.JsonProperty; 
import org.codehaus.jackson.map.ObjectMapper; 
import org.codehaus.jackson.map.PropertyNamingStrategy;*/ 
import org.junit.Assert; 
import org.junit.Test; 

public class JSONTest extends Assert { 

    @JsonIgnoreProperties(ignoreUnknown = true) 
    public static class Product { 

     @JsonProperty(value = "variationId") 
     private String variantId; 

     @JsonProperty(value = "price_text") 
     private String priceText; 

     @JsonProperty(value = "listPrice") 
     public String listPrice; 

     @JsonProperty(value = "PRODUCT_NAME") 
     public String name; 

     @JsonProperty(value = "Product_Desc") 
     public String description; 
    } 

    private static final String VALID_PRODUCT_JSON = 
      "{ \"list_price\": 289," + 
      " \"price_text\": \"269.00\"," + 
      " \"variation_id\": \"EUR\"," + 
      " \"product_name\": \"Product\"," + 
      " \"product_desc\": \"Test\"" + 
      "}"; 

    @Test 
    public void testDeserialization() throws IOException { 
     ObjectMapper mapper = new ObjectMapper(); 
     mapper.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES); 

     Product product = mapper.readValue(VALID_PRODUCT_JSON, Product.class); 
     System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(product)); 
     assertNotNull(product.listPrice); 
     assertNotNull(product.variantId); 
     assertNotNull(product.priceText); 
     assertNotNull(product.name); 
     assertNotNull(product.description); 
    } 
} 
+1

JSON是大小寫敏感的;具有不同外殼的鍵代表不同的事物。 Codehaus版本在這方面似乎不合規。 –

回答

8

@JsonProperty覆蓋任何PropertyNamingStrategyin fasterxml因爲2.4.0版本。但是,尚未發佈的版本2.7.0將提供一個feature以允許您重新選擇舊的行爲。還有一個未實現的suggestion可以在每個註釋級別切換這個,但是這對你沒有任何幫助。

在映射時,Codehaus確實在@JsonProperty值的頂部應用了PropertyNamingStrategy,儘管我找不到任何明確的文檔。這似乎也是2.4.0之前的fasterxml中的行爲。 Here是另一個有人注意到行爲差異的例子。

1

雖然SkinnyJ提供的解決方案非常適合您的問題,但如果您不能等到2.7發佈,您可以應用以下hack來解決問題。

這個想法是轉換傳入的JSON以匹配您的bean定義中的屬性。下面的代碼就是這樣。以下幾點應該注意:

  1. 如果你正在處理嵌套結構,你將不得不實現一個遞歸函數來實現這個轉換。
  2. 在進行轉換時會涉及一些小的開銷。

    公共類JSONTest延伸斷言{

    @JsonIgnoreProperties(ignoreUnknown = true) 
    public static class Product { 
    
        @JsonProperty(value = "variationId") 
        private String variantId; 
    
        @JsonProperty(value = "price_text") 
        private String priceText; 
    
        @JsonProperty(value = "listPrice") 
        public String listPrice; 
    
        @JsonProperty(value = "PRODUCT_NAME") 
        public String name; 
    
        @JsonProperty(value = "Product_Desc") 
        public String description; 
    } 
    
    private static final String VALID_PRODUCT_JSON = 
         "{ \"list_price\": 289," + 
         " \"price_text\": \"269.00\"," + 
         " \"variation_id\": \"EUR\"," + 
         " \"product_name\": \"Product\"," + 
         " \"product_desc\": \"Test\"" + 
         "}"; 
    
    @Test 
    public void testDeserialization() throws IOException { 
        ObjectMapper mapper = new ObjectMapper(); 
        mapper.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES); 
    
        //Capture the original JSON in org.json.JSONObject 
        JSONObject obj = new JSONObject(VALID_PRODUCT_JSON); 
        JSONArray keys = obj.names(); 
    
        //New json object to be created using property names defined in bean 
        JSONObject matchingJson = new JSONObject(); 
    
        //Map of lowercased key to original keys in incoming json. eg: Prod_id > prodid 
        Map<String, String> jsonMappings = new LinkedHashMap<String, String>(); 
        for (int i = 0; i < keys.length(); i++) { 
         String key = lowerCaseWithoutUnderScore(keys.getString(i)); 
         String value = keys.getString(i); 
         jsonMappings.put(key, value); 
        } 
    
        /* 
        * Iternate all jsonproperty beans and create new json 
        * such that keys in json map to that defined in bean 
        */ 
        Field[] fields = Product.class.getDeclaredFields(); 
        for (Field field : fields) { 
         JsonProperty prop = field.getAnnotation(JsonProperty.class); 
         String propNameInBean = prop.value(); 
         String keyToLook = lowerCaseWithoutUnderScore(propNameInBean); 
         String keyInJson = jsonMappings.get(keyToLook); 
         matchingJson.put(propNameInBean, obj.get(keyInJson)); 
        } 
    
        String json = matchingJson.toString(); 
        System.out.println(json); 
    
        //Pass the matching json to Object mapper 
        Product product = mapper.readValue(json, Product.class); 
        System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(product)); 
        assertNotNull(product.listPrice); 
        assertNotNull(product.variantId); 
        assertNotNull(product.priceText); 
        assertNotNull(product.name); 
        assertNotNull(product.description); 
    } 
    
    private String lowerCaseWithoutUnderScore(String key){ 
        return key.replaceAll("_", "").toLowerCase(); 
    } 
    

    }

相關問題