65

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.

Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
Mark Fellner
  • 651
  • 1
  • 5
  • 3

8 Answers8

128

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,447
  • 1
  • 22
  • 25
  • 1
    @deprecated Since 2.13. Now use: `JsonMapper.builder().configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)` – MrSmith42 Aug 16 '23 at 13:02
47

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
  • 777
  • 1
  • 7
  • 14
  • 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
  • 2
    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
15

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
  • 642
  • 6
  • 15
9

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

Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
8

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,922
  • 5
  • 82
  • 83
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;
            }
        }
    }

}
Marcus Voltolim
  • 413
  • 4
  • 12
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)
dilesh yadav
  • 81
  • 1
  • 3
-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>>(){});
  • 1
    Meaningless answer to question – Marcus Voltolim Mar 18 '22 at 19:23
  • 1
    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