From 89c532ab701df9659b039cbf2ba05f8008be3200 Mon Sep 17 00:00:00 2001 From: Lovekesh Garg Date: Sat, 14 Aug 2021 23:12:15 +0530 Subject: [PATCH 1/2] Add property to customize Jackson's default leniency See gh-27659 --- .../jackson/JacksonAutoConfiguration.java | 6 +++ .../jackson/JacksonProperties.java | 13 +++++ .../JacksonAutoConfigurationTests.java | 51 +++++++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.java index 915eb534c9f..d43bb686ffc 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.java @@ -187,6 +187,7 @@ public class JacksonAutoConfiguration { configurePropertyNamingStrategy(builder); configureModules(builder); configureLocale(builder); + configureLeniency(builder); } private void configureFeatures(Jackson2ObjectMapperBuilder builder, Map features) { @@ -289,6 +290,11 @@ public class JacksonAutoConfiguration { } } + private void configureLeniency(Jackson2ObjectMapperBuilder builder) { + Boolean lenient = this.jacksonProperties.getLenient(); + builder.postConfigurer(objectMapper -> objectMapper.setDefaultLeniency(lenient)); + } + private static Collection getBeans(ListableBeanFactory beanFactory, Class type) { return BeanFactoryUtils.beansOfTypeIncludingAncestors(beanFactory, type).values(); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonProperties.java index f11baa6f485..46c4d2ced5f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonProperties.java @@ -103,6 +103,11 @@ public class JacksonProperties { */ private Locale locale; + /** + * Setting for leniency, in case of absence it will be considered lenient = true; + */ + private Boolean lenient; + public String getDateFormat() { return this.dateFormat; } @@ -167,4 +172,12 @@ public class JacksonProperties { this.locale = locale; } + public Boolean getLenient() { + return lenient; + } + + public void setLenient(Boolean lenient) { + this.lenient = lenient; + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfigurationTests.java index 6c770b02988..10ddd64271c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfigurationTests.java @@ -26,6 +26,7 @@ import java.util.Set; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator.Mode; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; @@ -40,6 +41,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.exc.InvalidFormatException; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.util.StdDateFormat; import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; @@ -301,6 +303,40 @@ class JacksonAutoConfigurationTests { }); } + @Test + void disableLeniency() { + this.contextRunner.withPropertyValues("spring.jackson.lenient:false").run((context) -> { + boolean invalidFormat = false; + ObjectMapper mapper = context.getBean(ObjectMapper.class); + try { + mapper.readValue("{\"birthDate\": \"2010-12-30\"}", Person.class); + } + catch (InvalidFormatException e) { + assertThat(e).isNotNull(); + invalidFormat = true; + } + assertThat(invalidFormat).isTrue(); + }); + } + + @Test + void enableLeniency() { + this.contextRunner.withPropertyValues("spring.jackson.lenient:true").run((context) -> { + ObjectMapper mapper = context.getBean(ObjectMapper.class); + Person person = mapper.readValue("{\"birthDate\": \"2010-12-30\"}", Person.class); + assertThat(person.getBirthDate()).isNotNull(); + }); + } + + @Test + void defaultLeniency() { + this.contextRunner.run((context) -> { + ObjectMapper mapper = context.getBean(ObjectMapper.class); + Person person = mapper.readValue("{\"birthDate\": \"2010-12-30\"}", Person.class); + assertThat(person.getBirthDate()).isNotNull(); + }); + } + @Test void additionalJacksonBuilderCustomization() { this.contextRunner.withUserConfiguration(ObjectMapperBuilderCustomConfig.class).run((context) -> { @@ -537,4 +573,19 @@ class JacksonAutoConfigurationTests { } + static class Person { + + @JsonFormat(pattern = "yyyyMMdd") + private Date birthDate; + + public Date getBirthDate() { + return birthDate; + } + + public void setBirthDate(Date birthDate) { + this.birthDate = birthDate; + } + + } + } From 68a47a7f114d5a151690dc4b84aae1131026e971 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 16 Aug 2021 08:56:26 +0200 Subject: [PATCH 2/2] Polish "Add property to customize Jackson's default leniency" See gh-27659 --- .../jackson/JacksonAutoConfiguration.java | 10 +++-- .../jackson/JacksonProperties.java | 28 +++++++------- .../JacksonAutoConfigurationTests.java | 38 ++++++------------- 3 files changed, 32 insertions(+), 44 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.java index d43bb686ffc..a1318c45191 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.java @@ -187,7 +187,7 @@ public class JacksonAutoConfiguration { configurePropertyNamingStrategy(builder); configureModules(builder); configureLocale(builder); - configureLeniency(builder); + configureDefaultLeniency(builder); } private void configureFeatures(Jackson2ObjectMapperBuilder builder, Map features) { @@ -290,9 +290,11 @@ public class JacksonAutoConfiguration { } } - private void configureLeniency(Jackson2ObjectMapperBuilder builder) { - Boolean lenient = this.jacksonProperties.getLenient(); - builder.postConfigurer(objectMapper -> objectMapper.setDefaultLeniency(lenient)); + private void configureDefaultLeniency(Jackson2ObjectMapperBuilder builder) { + Boolean defaultLeniency = this.jacksonProperties.getDefaultLeniency(); + if (defaultLeniency != null) { + builder.postConfigurer((objectMapper) -> objectMapper.setDefaultLeniency(defaultLeniency)); + } } private static Collection getBeans(ListableBeanFactory beanFactory, Class type) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonProperties.java index 46c4d2ced5f..fa9e17e9124 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jackson/JacksonProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -92,6 +92,11 @@ public class JacksonProperties { */ private JsonInclude.Include defaultPropertyInclusion; + /** + * Global default setting (if any) for leniency. + */ + private Boolean defaultLeniency; + /** * Time zone used when formatting dates. For instance, "America/Los_Angeles" or * "GMT+10". @@ -103,11 +108,6 @@ public class JacksonProperties { */ private Locale locale; - /** - * Setting for leniency, in case of absence it will be considered lenient = true; - */ - private Boolean lenient; - public String getDateFormat() { return this.dateFormat; } @@ -156,6 +156,14 @@ public class JacksonProperties { this.defaultPropertyInclusion = defaultPropertyInclusion; } + public Boolean getDefaultLeniency() { + return this.defaultLeniency; + } + + public void setDefaultLeniency(Boolean defaultLeniency) { + this.defaultLeniency = defaultLeniency; + } + public TimeZone getTimeZone() { return this.timeZone; } @@ -172,12 +180,4 @@ public class JacksonProperties { this.locale = locale; } - public Boolean getLenient() { - return lenient; - } - - public void setLenient(Boolean lenient) { - this.lenient = lenient; - } - } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfigurationTests.java index 10ddd64271c..3d2856d576f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -59,6 +59,7 @@ import org.springframework.context.annotation.Primary; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.mock; /** @@ -304,24 +305,8 @@ class JacksonAutoConfigurationTests { } @Test - void disableLeniency() { - this.contextRunner.withPropertyValues("spring.jackson.lenient:false").run((context) -> { - boolean invalidFormat = false; - ObjectMapper mapper = context.getBean(ObjectMapper.class); - try { - mapper.readValue("{\"birthDate\": \"2010-12-30\"}", Person.class); - } - catch (InvalidFormatException e) { - assertThat(e).isNotNull(); - invalidFormat = true; - } - assertThat(invalidFormat).isTrue(); - }); - } - - @Test - void enableLeniency() { - this.contextRunner.withPropertyValues("spring.jackson.lenient:true").run((context) -> { + void enableDefaultLeniency() { + this.contextRunner.withPropertyValues("spring.jackson.default-leniency:true").run((context) -> { ObjectMapper mapper = context.getBean(ObjectMapper.class); Person person = mapper.readValue("{\"birthDate\": \"2010-12-30\"}", Person.class); assertThat(person.getBirthDate()).isNotNull(); @@ -329,11 +314,12 @@ class JacksonAutoConfigurationTests { } @Test - void defaultLeniency() { - this.contextRunner.run((context) -> { + void disableDefaultLeniency() { + this.contextRunner.withPropertyValues("spring.jackson.default-leniency:false").run((context) -> { ObjectMapper mapper = context.getBean(ObjectMapper.class); - Person person = mapper.readValue("{\"birthDate\": \"2010-12-30\"}", Person.class); - assertThat(person.getBirthDate()).isNotNull(); + assertThatThrownBy(() -> mapper.readValue("{\"birthDate\": \"2010-12-30\"}", Person.class)) + .isInstanceOf(InvalidFormatException.class).hasMessageContaining("expected format") + .hasMessageContaining("yyyyMMdd"); }); } @@ -578,11 +564,11 @@ class JacksonAutoConfigurationTests { @JsonFormat(pattern = "yyyyMMdd") private Date birthDate; - public Date getBirthDate() { - return birthDate; + Date getBirthDate() { + return this.birthDate; } - public void setBirthDate(Date birthDate) { + void setBirthDate(Date birthDate) { this.birthDate = birthDate; }