52

Does anyone know how com.fasterxml.jackson.databind.ObjectMapper is able to map JSON properties to POJO properties case insensitive?

JSON-String:

[{"FIRSTNAME":"John","LASTNAME":"Doe","DATEOFBIRTH":"1980-07-16T18:25:00.000Z"}]

POJO-Class:

public class Person {

    private String firstName;
    private String lastName;
    private Date dateOfBirth;

    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public Date getDateOfBirth() {
        return dateOfBirth;
    }
    public void setDateOfBirth(Date dateOfBirth) {
        this.dateOfBirth = dateOfBirth;
    }
}

Test-Class:

@Test
public final void testDeserializingPersonJsonToPersonClass()
        throws JsonParseException, JsonMappingException, IOException {
    final String jsonAsString = "[{\"FIRSTNAME\":\"John\",\"LASTNAME\":\"Doe\",\"DATEOFBIRTH\":\"1980-07-16T18:25:00.000Z\"}]";
    final ObjectMapper mapper = new ObjectMapper();

    final Person person = mapper.readValue(jsonAsString, Person.class);

    assertNotNull(person);
    assertThat(person.getFirstName(), equalTo("John"));
}

This ends up in following error:
com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of ...

It's not possible to change neither JSON-String nor POJO-Class.

Mark Fellner
  • 521
  • 1
  • 4
  • 3

6 Answers6

114

This behaviour was introduced in Jackson 2.5.0. You can configure the mapper to be case insensitive using MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES.

For example :

ObjectMapper mapper = new ObjectMapper();
mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
Nicolas Riousset
  • 3,227
  • 1
  • 20
  • 24
36

You can solve this problem by configuring the mapper, as described by the @Nicolas Riousset.

In addition, since version Jackson 2.9 you can do the same using annotation @JsonFormat(with = JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES) over a field or class, which is a more flexible option.

@JsonFormat(with = JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)
private String firstName;
Dmitry Kokora
  • 608
  • 1
  • 7
  • 12
  • 1
    I tried this in a Spring Boot application that uses jackson 2.9 at minimum and it doesn't seem to have helped. Is my use case different? Im optimistic that there is a solution to this but I haven't found anything yet.... I want to believe :) – user1445967 Jun 21 '19 at 17:09
  • @user1445967, how do you use this feature? Do you have any additional Jackson mapping configuration? – Dmitry Kokora Jun 23 '19 at 12:24
  • I am talking about 'ordinary' `@RequestMapping / @GetMapping / @PostMapping` in Spring [Boot] MVC where the `@RequestBody` annotation is placed before an object that is meant to store the JSON properties. For this reason we do not (intentionally) try to configure any part of Jackson... – user1445967 Jun 24 '19 at 18:16
  • 1
    As of May 2020, You CANNOT apply this mapper feature at class level. Such feature is still not implemented. Currently scheduled for Jackson version 2.12 https://github.com/FasterXML/jackson-databind/issues/1886 – Michał Maciej Gałuszka May 13 '20 at 09:23
  • No, the example does not work. The annotation only applies to properties in object of annotated element, not an annotated property itself. There is no single-property case-insensitivity! – holmis83 Dec 10 '20 at 16:19
  • ugh indeed doesn't work, but something similar according to this does work: https://stackoverflow.com/questions/47611062/using-resttemplate-to-map-json-to-object/47612570 – hello_earth Aug 04 '21 at 11:21
  • 1
    This worked for me even when I applied it to the entire class. (The line above the class). – IcyIcicle Feb 08 '22 at 00:23
  • 1
    Worked for me as well. Thank you @DmitryKokora – Xequtor Apr 06 '22 at 10:14
14

I had the same problem and couldn't find a global way of solving this. However you can have 2 setters per property to achieve this:

@JsonSetter("FIRSTNAME")
public void setFirstNameCaps(String firstName) {
    this.firstName = firstName;
}

@JsonSetter("firstName")
public void setFirstName(String firstName) {
    this.firstName = firstName;
}

Not elegant but will work for both upper and lower case json fields. You can also try the solution mentioned here but this might have a performance overhead

Dhananjay
  • 594
  • 4
  • 15
6
package br.com.marcusvoltolim.util;


import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.log4j.Log4j;

@Log4j
public class JsonUtils {

    private static final ObjectMapper OBJECT_MAPPER;

    static {
        OBJECT_MAPPER = new ObjectMapper();
        OBJECT_MAPPER.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
        OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    public static <T> T fromJson(final String json, final Class<T> classe) {
        try {
            return OBJECT_MAPPER.readValue(json, classe);
        } catch (Exception e) {
            log.error(e);
            try {
                return classe.newInstance();
            } catch (InstantiationException | IllegalAccessException ex) {
                return null;
            }
        }
    }

}
6

As of Jackson version 2.12, you can finally annotate on class:

@JsonFormat(with = JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)
public class Person {
    private String firstName;
    private String lastName;
    private Date dateOfBirth;
    // setters and getters ...
}

As noted in the Github issue, there is still no support for single-property case-insensitivity!

holmis83
  • 15,035
  • 4
  • 74
  • 82
-1

I was in the same kind of situation and had to convert to a map and then copy the values over manually.

import com.fasterxml.jackson.core.type.TypeReference;

Map<String, String> map = 
    mapper.readValue(jsonAsString, new TypeReference<Map<String, String>>(){});
  • Meaningless answer to question – Marcus Vinícius Voltolim Mar 18 '22 at 19:23
  • Please read "[answer]" and "[Explaining entirely code-based answers](https://meta.stackoverflow.com/q/392712/128421)". It helps more if you supply an explanation why this is the preferred solution and explain how it works. We want to educate, not just provide code. – the Tin Man Mar 20 '22 at 22:45