How can I loosen up the naming strategy when deserializing using Jackson?
@JsonProperty
overrides any PropertyNamingStrategy
in fasterxml since version 2.4.0. However, yet-to-be-released version 2.7.0 will provide a feature to allow you to opt back in to the old behavior. There is also an unimplemented suggestion to toggle this at the per-annotation level, but that would not really help you.
It appears that Codehaus does apply the PropertyNamingStrategy
on top of the @JsonProperty
values when mapping, although I can't find any clear docs on that. This appears to have been the behavior in fasterxml before 2.4.0 as well. Here is another example of someone noticing the same difference in behavior.
Although the solution provided by SkinnyJ is perfect for your problem, but if you can't wait till 2.7 is released, you can apply the below hack to get around the problem.
The idea is to transform the incoming JSON to match the attributes in your bean definition. Below code does that. Following points should be noted:
- If you are dealing with nested structures, you will have to implement a recursive function to achieve this transformation.
- There is a little overhead involved in doing the transformation.
Code:
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(); }}