2015-11-13 3 views
9

Я пытался обновить модули JSON, чтобы использовать версии Jackson вместо FasterXML (2.6.3) вместо старых модулей Codehaus. Во время обновления я заметил, что стратегия именования отличается при использовании FasterXML вместо Codehaus.Как я могу ослабить стратегию именования при десериализации с помощью Джексона?

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 применяет PropertyNamingStrategy поверх значений @JsonProperty при сопоставлении, хотя я не могу найти никаких четких документов по этому вопросу. По-видимому, это было поведение в quickxml до 2.4.0. Here - еще один пример того, кто заметил такую ​​же разницу в поведении.

1

Хотя решение, предоставляемое SkinnyJ, идеально подходит для вашей проблемы, но если вы не можете дождаться выхода 2.7, вы можете применить ниже описанный хак, чтобы обойти проблему.

Идея состоит в том, чтобы преобразовать входящий JSON в соответствие с атрибутами в определении компонента. Ниже приведен код. Следует отметить следующие моменты:

  1. Если вы имеете дело со вложенными структурами, вам необходимо реализовать рекурсивную функцию для достижения этого преобразования.
  2. Существует небольшое накладные расходы, связанные с трансформацией.

    общественного класса JSONTest расширяет 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(); 
    } 
    

    }

Смежные вопросы