2015-11-13 21 views
9

Próbowałem uaktualnić moduły JSON, aby korzystać z wersji Jackson FasterXML (2.6.3) zamiast starych modułów Codehaus. Podczas aktualizacji zauważyłem, że strategia nazewnictwa różni się w przypadku używania FasterXML zamiast Codehaus.Jak mogę poluzować strategię nazewnictwa podczas deserializacji przy użyciu Jacksona?

Codehaus był bardziej elastyczny, jeśli chodzi o strategię nazewnictwa. Poniższy test pokazuje problem, z jakim mam do czynienia przy pomocy FasterXML. Jak mogę skonfigurować ObjectMapper, aby był zgodny z tą samą strategią jak Codehaus?

Nie mogę zmienić adnotacji JSONProperty, ponieważ są ich setki. Chciałbym, aby uaktualnienie było zgodne wstecz ze strategią nazewnictwa.

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 jest rozróżniana; klucze o różnej obudowie reprezentują różne rzeczy. Wygląda na to, że wersja Codehaus nie była w tym względzie niezgodna. –

Odpowiedz

8

@JsonProperty zastępuje wszelkie PropertyNamingStrategyin fasterxml od wersji 2.4.0. Jednak jeszcze niedługo wydana wersja 2.7.0 zapewni feature, aby umożliwić powrót do starego zachowania. Istnieje również niezaimplementowany suggestion, aby włączyć tę opcję na poziomie adnotacji, ale to naprawdę nie pomoże.

Wygląda na to, że Codehaus stosuje PropertyNamingStrategy na górze wartości @JsonProperty podczas mapowania, chociaż nie mogę znaleźć żadnych wyraźnych dokumentów na ten temat. Wydaje się, że było to zachowanie w szybszymxml przed 2.4.0. Here to kolejny przykład zauważenia przez kogoś tej samej różnicy w zachowaniu.

1

Chociaż rozwiązanie dostarczone przez SkinnyJ jest idealne dla twojego problemu, ale jeśli nie możesz się doczekać, aż zwolni się 2.7, możesz zastosować poniższy hack, aby obejść problem.

Chodzi o to, aby przekształcić przychodzący JSON, aby pasował do atrybutów w definicji komponentu bean. Poniższy kod to robi. Należy zwrócić uwagę na następujące punkty:

  1. Jeśli masz do czynienia ze strukturami zagnieżdżonymi, będziesz musiał zaimplementować funkcję rekursywną, aby osiągnąć tę transformację.
  2. W transformacji zaangażowanych jest niewiele wysiłku.

    klasy publicznej JSONTest rozciąga Stwierdź {

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

    }