2015-11-13 13 views
9

Ich habe versucht, die JSON-Module zu aktualisieren, um die FasterXML (2.6.3) -Versionen von Jackson anstelle der alten Codehaus-Module zu verwenden. Während des Upgrades ist mir aufgefallen, dass die Benennungsstrategie bei der Verwendung von FasterXML anstelle von Codehaus unterschiedlich ist.Wie kann ich die Namensauswahl beim Deserialisieren mit Jackson lockern?

Codehaus war flexibler, wenn es um die Benennungsstrategie ging. Der folgende Test zeigt das Problem mit FasterXML. Wie kann ich die ObjectMapper so konfigurieren, dass sie der gleichen Strategie wie Codehaus folgt?

Ich kann die JSONProperty Anmerkungen nicht ändern, da es Hunderte von ihnen gibt. Ich möchte, dass das Upgrade in Bezug auf die Benennungsstrategie abwärtskompatibel ist.

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 ist Groß-; Schlüssel mit unterschiedlichem Gehäuse repräsentieren verschiedene Dinge. Es scheint, dass die Codehaus-Version in dieser Hinsicht nicht konform war. –

Antwort

8

@JsonProperty überschreibt jede PropertyNamingStrategyin fasterxml seit Version 2.4.0. Die noch zu veröffentlichende Version 2.7.0 bietet jedoch eine feature, mit der Sie sich wieder für das alte Verhalten entscheiden können. Es gibt auch eine nicht implementierte suggestion, um dies auf die Annotations-Ebene zu schalten, aber das würde Ihnen nicht wirklich helfen.

Es scheint, dass Codehaus die PropertyNamingStrategy auf die @JsonProperty Werte beim Mapping anwenden, obwohl ich keine klaren Dokumente darüber finden kann. Dies scheint auch das Verhalten in fasterxml vor 2.4.0 gewesen zu sein. Here ist ein weiteres Beispiel für jemanden, der den gleichen Unterschied im Verhalten bemerkt.

1

Obwohl die von SkinnyJ zur Verfügung gestellte Lösung perfekt für Ihr Problem ist, aber wenn Sie nicht warten können, bis 2.7 veröffentlicht wird, können Sie den folgenden Hack anwenden, um das Problem zu umgehen.

Die Idee besteht darin, den eingehenden JSON so zu transformieren, dass er den Attributen in Ihrer Bean-Definition entspricht. Unter Code tut das. Folgende Punkte sollten beachtet werden:

  1. Wenn Sie mit verschachtelten Strukturen arbeiten, müssen Sie eine rekursive Funktion implementieren, um diese Transformation zu erreichen.
  2. Es ist ein kleiner Aufwand erforderlich, um die Transformation durchzuführen.

    public class JSONTest erstreckt 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); 
    
        //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(); 
    } 
    

    }

Verwandte Themen