13

I'm upgrading the version of my project and I am currently using jackson-databind-2.13.0 .But I noticed that ObjectMapper's enable method is deprecated.

They said to use it like this instead.

@deprecated Since 2.13 use {@code JsonMapper.builder().enable(...)}

But I couldn't use it.

Below is my ObjectMapper instance creation code. how can I change?

      @Bean(name = {"objectMapper"})
      @Primary
      ObjectMapper objectMapper() {
        return newObjectMapper();
      }

  public static ObjectMapper newObjectMapper() {
    ObjectMapper objectMapper =
        new ObjectMapper()
            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
            .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
            .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true)
            .setSerializationInclusion(JsonInclude.Include.NON_NULL)
            .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(OffsetDateTime.class, new OffsetDateTimeSerializer());
javaTimeModule.addDeserializer(OffsetDateTime.class, new OffsetDateTimeDeserializer());
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer());
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer());
objectMapper
    .registerModule(javaTimeModule)
    .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

return objectMapper;

}

Solution:

    ObjectMapper objectMapper = JsonMapper
    .builder()
    .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)
    .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
    .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
    .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
    .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true)
    .serializationInclusion(Include.NON_NULL).build();
Sezer Aydın
  • 187
  • 1
  • 2
  • 10
  • Why couldn't you use it? Did you get an error? Was the result unexpected? The deprecation comment pretty clearer states that `new ObjectMapper().enable(…)` should be converted to `JsonMapper.builder().enable(…).build()` – knittl Jan 06 '22 at 08:02
  • 1
    Why aren't you using the Spring Boot configured instance and let Spring Boot configure it (and you provide some properties to configure it as you want? – M. Deinum Jan 06 '22 at 08:02
  • being deprecated doesn't mean you can't use it, it means you shouldn't use it. As in: yes, you can still use it now, but you should refactor your code before the method is removed from a new(er) version of the library – Stultuske Jan 06 '22 at 08:02
  • I actually use it, I don't get any errors. I just want to change. – Sezer Aydın Jan 06 '22 at 08:04
  • You are using what? This method or the Spring Boot configured one (as that you aren't as you are configuring it yourself). – M. Deinum Jan 06 '22 at 08:16
  • how can i use JsonMapper.builder().enable(...) instead of .enable(..) There are no examples in the jackson-databind docs. I want to change something that is deprecated. – Sezer Aydın Jan 06 '22 at 08:21
  • @SezerAydın this might be me not understanding you, but the answer to "how can I use `JsonMapper.builder().enable(…)`" is "you use `JsonMapper.builder().enable(…)`". You replace the old code with the new code, as instructed by the deprecation note. The JavaDoc or your IDE's auto-completion will help you know which methods are available on the builder instance (I assume most methods will have identical names to make migration easy). – knittl Jan 06 '22 at 08:34
  • I misspelled the first comment. currently, I am not using JsonMapper. Therefore, I do not use its methods. Now let me explain the other part. I am using enable(..) method of objectmapper. and this method has been deprecated after version 2.13. And yes you are right i can use this method. but i dont want to use. I will use the JsonMapper.builder.enable(..) method as stated in the documentation. – Sezer Aydın Jan 06 '22 at 08:53
  • i've added my ObjectMapperConfiguration class – Sezer Aydın Jan 06 '22 at 09:00
  • @SezerAydın I still don't understand your question. You replace `new ObjectMapper().enable(…)` with `JsonMapper.builder().enable(…).build()`. What exactly is your problem with the new code? Errors, exceptions, unexpected results? – knittl Jan 06 '22 at 09:03
  • My fault, I didn't look carefully. The JsonMapper class is being extended from ObjectMapper. – Sezer Aydın Jan 06 '22 at 09:25
  • Thank you all and @knittl. I've added solution. – Sezer Aydın Jan 06 '22 at 09:27

2 Answers2

12

I would suggest to rewrite your code to either

  1. Remove this bean and use a fully Spring Boot configured ObjectMapper (which has the name jacksonObjectMapper)
  2. Use the Jackson2ObjectMapperBuilder to create an instance of the ObjectMapper.

All of these solutions hide the intricate parts of constructing the ObjectMapper and will also put the burden of constructing it (properly) on the Spring Boot team, instead of you.

Now for option 1 you would need to remove your @Bean and place the following in your application.properties.

spring.jackson.serialization.FAIL_ON_EMPTY_BEANS=false
spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS=false

spring.jackson.deserialization.FAIL_ON_UNKNOWN_PROPERTIES=false
spring.jackson.deserialization.ACCEPT_SINGLE_VALUE_AS_ARRAY=true

spring.jackson.mapper.ACCEPT_CASE_INSENSITIVE_PROPERTIES=true

spring.jackson.defaultPropertyInclusion=NON_NULL

When Spring (Boot) detects the JavaTime module on the classpath it will automatically be registered with the ObjectMapper, so no need to additionally add that (or the serializers for that matter).

These lines of configuration should you give the same configured ObjectMapper as your explicitly configured one. H

For the second option you can inject the Jackson2ObjectMapperBuilder into the method by using an argument, configure the things you want on there and call the build method in the end.

@Bean(name = {"objectMapper"})
@Primary
ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
    return newObjectMapper(builder);
}

public static ObjectMapper newObjectMapper(Jackson2ObjectMapperBuilder builder) {
   return builder
            .serializationInclusion(NON_NULL)
            .failOnEmptyBeans(false)
            .failOnUnknownProperties(false)
           .featuresToEnable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
           .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.build();

You still don't need to register the JavaTime module as that is still being auto-detected for you.

In theory you could combine 1 and 2 but in your case that wouldn't add much, only some code to construct the ObjectMapper.

@Bean(name = {"objectMapper"})
@Primary
ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
    return newObjectMapper(builder);
}

public static ObjectMapper newObjectMapper(Jackson2ObjectMapperBuilder builder) {
   return builder.build();
}
M. Deinum
  • 115,695
  • 22
  • 220
  • 224
3

I had a similar problem while trying to enable Enum case insensitive deserialization through MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS.

In my case switching to the builder approach was not possible due to ObjectMapper being previously constructed by an external source. So the only option was to modify ObjectMapper deserialization config directly. Searching for alternatives to avoid deprecated API objectMapper.enable(MappedFeature... f) I found a workaround by...

  • Getting the original DeserializationConfig from the ObjectMapper instance.
  • Creating a 'copy' of the original config with MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS enabled by calling with(MapperFeature... features) on the config's instance.
  • Replacing the ObjectMapper original config with the new one through the setter method.

This also applies to SerializationConfig and it is a way of modifying configurations on an already constructed ObjectMapper without using deprecated APIs.

public void customizeMapper(ObjectMapper objectMapper) {
    final DeserializationConfig originalConfig = objectMapper.getDeserializationConfig();

    final DeserializationConfig newConfig = originalConfig.with(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS);

    objectMapper.setConfig(newConfig);
}

Note: Using Jackson's builder is definitely a better solution. Consider this approach only if using the builder is not possible.