diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/DataObjectPropertyName.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/DataObjectPropertyName.java index 9839859a7e4..aa0401347b8 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/DataObjectPropertyName.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/DataObjectPropertyName.java @@ -21,9 +21,10 @@ package org.springframework.boot.context.properties.bind; * * @author Phillip Webb * @author Madhura Bhave + * @since 2.2.3 * @see DataObjectBinder */ -abstract class DataObjectPropertyName { +public abstract class DataObjectPropertyName { private DataObjectPropertyName() { } @@ -33,7 +34,7 @@ abstract class DataObjectPropertyName { * @param name the source name * @return the dashed from */ - static String toDashedForm(String name) { + public static String toDashedForm(String name) { StringBuilder result = new StringBuilder(); String replaced = name.replace('_', '-'); for (int i = 0; i < replaced.length(); i++) { diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandler.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandler.java index d51cebb0695..30982e223b8 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandler.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandler.java @@ -28,6 +28,7 @@ import org.springframework.boot.context.properties.bind.AbstractBindHandler; import org.springframework.boot.context.properties.bind.BindContext; import org.springframework.boot.context.properties.bind.BindHandler; import org.springframework.boot.context.properties.bind.Bindable; +import org.springframework.boot.context.properties.bind.DataObjectPropertyName; import org.springframework.boot.context.properties.source.ConfigurationProperty; import org.springframework.boot.context.properties.source.ConfigurationPropertyName; import org.springframework.core.ResolvableType; @@ -187,7 +188,7 @@ public class ValidationBindHandler extends AbstractBindHandler { } private ConfigurationPropertyName getName(String field) { - return this.name.append(field); + return this.name.append(DataObjectPropertyName.toDashedForm(field)); } ValidationErrors getValidationErrors() { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandlerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandlerTests.java index 2c0c17c846b..1c1cc730acb 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandlerTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandlerTests.java @@ -193,6 +193,14 @@ class ValidationBindHandlerTests { .satisfies((ex) -> assertThat(ex.getCause()).hasMessageContaining("years")); } + @Test + void validationErrorsForCamelCaseFieldsShouldContainRejectedValue() { + this.sources.add(new MockConfigurationPropertySource("foo.inner.person-age", 2)); + BindValidationException cause = bindAndExpectValidationError(() -> this.binder + .bind(ConfigurationPropertyName.of("foo"), Bindable.of(ExampleCamelCase.class), this.handler)); + assertThat(cause.getMessage()).contains("rejected value [2]"); + } + private BindValidationException bindAndExpectValidationError(Runnable action) { try { action.run(); @@ -305,6 +313,33 @@ class ValidationBindHandlerTests { } + @Validated + static class ExampleCamelCase { + + @Valid + private InnerProperties inner = new InnerProperties(); + + InnerProperties getInner() { + return this.inner; + } + + static class InnerProperties { + + @Min(5) + private int personAge; + + int getPersonAge() { + return this.personAge; + } + + void setPersonAge(int personAge) { + this.personAge = personAge; + } + + } + + } + @Validated static class ExampleValidatedBeanWithGetterException {