71

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 a JsonMappingException:

com.fasterxml.jackson.databind.JsonMappingException:
Can not deserialize instance of ...

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

1
  • 1
    It's not possible to change neither JSON-String nor POJO-Class, then it's not possible. Commented Sep 26, 2014 at 11:51

8 Answers 8

133

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);
1
  • 3
    @deprecated Since 2.13. Now use: JsonMapper.builder().configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)
    – MrSmith42
    Commented Aug 16, 2023 at 13:02
50

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;
7
  • 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 :) Commented Jun 21, 2019 at 17:09
  • @user1445967, how do you use this feature? Do you have any additional Jackson mapping configuration? Commented Jun 23, 2019 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... Commented Jun 24, 2019 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 github.com/FasterXML/jackson-databind/issues/1886 Commented May 13, 2020 at 9:23
  • 2
    This worked for me even when I applied it to the entire class. (The line above the class).
    – IcyIcicle
    Commented Feb 8, 2022 at 0:23
17

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

15

JsonMapper.builder().configure(...)

Method ObjectMapper.configure() has been deprecated since Jackson's release version 2.13.

Deprecated. Since 2.13 use JsonMapper.builder().enable(...)

The recommended approach is to one of use overloaded versions of configure() method, which is coming from MapperBuilder (parent of JsonMapper.Builder class)

Example:

ObjectMapper mapper = JsonMapper.builder()
    .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)
    .build();

Spring Boot - Application Properties

In a Spring Boot application, this customization can be specified through the Common Application Properties inside the application.properties file (or application.yml file).

spring.jackson.mapper.ACCEPT_CASE_INSENSITIVE_PROPERTIES=true

Spring Boot would take care about applying specified feature while configuring ObjectMapper at the application startup (also through properties you can provide, or turn on/off other Jackson-related properties like default locale, serialization features, etc.).

10

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!

2
7
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;
            }
        }
    }

}
1

In case someone wants to handle this at field level in the POJO itself, they can use either of two annotations with

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MMM-yyyy",with = JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_VALUES)

Or

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MMM-yyyy",with = JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)
-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>>(){});
2

Not the answer you're looking for? Browse other questions tagged or ask your own question.