Как я могу ослабить стратегию именования при десериализации с использованием Джексона?

Я пытался обновить модули 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);
    }
}

person Mridang Agarwalla    schedule 13.11.2015    source источник
comment
JSON чувствителен к регистру; ключи с разным корпусом представляют разные вещи. Похоже, что версия Codehaus не соответствовала требованиям в этом отношении.   -  person Tom G    schedule 13.11.2015


Ответы (2)


@JsonProperty переопределяет любые PropertyNamingStrategy в fastxml, начиная с версии 2.4.0. Однако еще не выпущенная версия 2.7.0 будет содержать feature, чтобы вы могли вернуться к старому поведению. Существует также нереализованное предложение переключать это на уровне аннотаций, но на самом деле это не поможет. ты.

Похоже, что Codehaus применяет значения PropertyNamingStrategy поверх значений @JsonProperty при отображении, хотя я не могу найти никаких четких документов по этому поводу. Похоже, что это было поведение в fastxml и до 2.4.0. Вот еще один пример того, как кто-то заметил ту же разницу в поведение.

person Yosef Weiner    schedule 16.11.2015

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

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

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

Код:

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);

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

}
person Mohit    schedule 23.11.2015