Expand support for adapting container type violations
See gh-31530
This commit is contained in:
committed by
rstoyanchev
parent
f16122d533
commit
9bbe3aa52a
@@ -302,7 +302,7 @@ public class MethodValidationAdapter implements MethodValidator {
|
||||
Function<Integer, Object> argumentFunction) {
|
||||
|
||||
Map<MethodParameter, ValueResultBuilder> parameterViolations = new LinkedHashMap<>();
|
||||
Map<Path.Node, BeanResultBuilder> cascadedViolations = new LinkedHashMap<>();
|
||||
Map<CascadedViolationsKey, BeanResultBuilder> cascadedViolations = new LinkedHashMap<>();
|
||||
|
||||
for (ConstraintViolation<Object> violation : violations) {
|
||||
Iterator<Path.Node> itr = violation.getPropertyPath().iterator();
|
||||
@@ -329,7 +329,8 @@ public class MethodValidationAdapter implements MethodValidator {
|
||||
}
|
||||
else {
|
||||
cascadedViolations
|
||||
.computeIfAbsent(node, n -> new BeanResultBuilder(parameter, argument, itr.next()))
|
||||
.computeIfAbsent(new CascadedViolationsKey(node, violation.getLeafBean()),
|
||||
n -> new BeanResultBuilder(parameter, argument, itr.next(), violation.getLeafBean()))
|
||||
.addViolation(violation);
|
||||
}
|
||||
break;
|
||||
@@ -338,7 +339,7 @@ public class MethodValidationAdapter implements MethodValidator {
|
||||
|
||||
List<ParameterValidationResult> validatonResultList = new ArrayList<>();
|
||||
parameterViolations.forEach((parameter, builder) -> validatonResultList.add(builder.build()));
|
||||
cascadedViolations.forEach((node, builder) -> validatonResultList.add(builder.build()));
|
||||
cascadedViolations.forEach((violationsKey, builder) -> validatonResultList.add(builder.build()));
|
||||
validatonResultList.sort(resultComparator);
|
||||
|
||||
return MethodValidationResult.create(target, method, validatonResultList);
|
||||
@@ -372,6 +373,14 @@ public class MethodValidationAdapter implements MethodValidator {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* A unique key for the cascaded violations map. Individually, the node and leaf bean may not be unique for all
|
||||
* collection types ({@link Set} will have the same node and {@link List} may have the same leaf), but together
|
||||
* they should represent a distinct pairing.
|
||||
* @param node the path of the violation
|
||||
* @param leafBean the validated object
|
||||
*/
|
||||
record CascadedViolationsKey(Path.Node node, Object leafBean) { }
|
||||
|
||||
/**
|
||||
* Strategy to resolve the name of an {@code @Valid} method parameter to
|
||||
@@ -446,25 +455,20 @@ public class MethodValidationAdapter implements MethodValidator {
|
||||
|
||||
private final Set<ConstraintViolation<Object>> violations = new LinkedHashSet<>();
|
||||
|
||||
public BeanResultBuilder(MethodParameter parameter, @Nullable Object argument, Path.Node node) {
|
||||
public BeanResultBuilder(MethodParameter parameter, @Nullable Object argument, Path.Node node, @Nullable Object leafBean) {
|
||||
this.parameter = parameter;
|
||||
|
||||
this.containerIndex = node.getIndex();
|
||||
this.containerKey = node.getKey();
|
||||
if (argument instanceof List<?> list && this.containerIndex != null) {
|
||||
this.container = list;
|
||||
argument = list.get(this.containerIndex);
|
||||
}
|
||||
else if (argument instanceof Map<?, ?> map && this.containerKey != null) {
|
||||
this.container = map;
|
||||
argument = map.get(this.containerKey);
|
||||
if (argument != null && !argument.equals(leafBean)) {
|
||||
this.container = argument;
|
||||
}
|
||||
else {
|
||||
this.container = null;
|
||||
}
|
||||
|
||||
this.argument = argument;
|
||||
this.errors = createBindingResult(parameter, argument);
|
||||
this.argument = leafBean;
|
||||
this.errors = createBindingResult(parameter, leafBean);
|
||||
}
|
||||
|
||||
public void addViolation(ConstraintViolation<Object> violation) {
|
||||
|
||||
@@ -32,11 +32,10 @@ import org.springframework.validation.ObjectError;
|
||||
* {@link Errors#getAllErrors()}, but this subclass provides access to the same
|
||||
* as {@link FieldError}s.
|
||||
*
|
||||
* <p>When the method parameter is a {@link List} or {@link java.util.Map},
|
||||
* a separate {@link ParameterErrors} is created for each list or map value for
|
||||
* which there are validation errors. In such cases, the {@link #getContainer()}
|
||||
* method returns the list or map, while {@link #getContainerIndex()}
|
||||
* and {@link #getContainerKey()} return the value index or key.
|
||||
* <p>When the method parameter is a multi-element container like {@link List} or
|
||||
* {@link java.util.Map}, a separate {@link ParameterErrors} is created for each
|
||||
* value for which there are validation errors. Otherwise, only a single
|
||||
* {@link ParameterErrors} will be created.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 6.1
|
||||
@@ -71,11 +70,12 @@ public class ParameterErrors extends ParameterValidationResult implements Errors
|
||||
|
||||
|
||||
/**
|
||||
* When {@code @Valid} is declared on a {@link List} or {@link java.util.Map}
|
||||
* method parameter, this method returns the list or map that contained the
|
||||
* validated object {@link #getArgument() argument}, while
|
||||
* {@link #getContainerIndex()} and {@link #getContainerKey()} returns the
|
||||
* respective index or key.
|
||||
* When {@code @Valid} is declared on a container type method parameter such as
|
||||
* {@link java.util.Collection}, {@link java.util.Optional} or {@link java.util.Map},
|
||||
* this method returns the parent that contained the validated object
|
||||
* {@link #getArgument() argument}, while {@link #getContainerIndex()} and
|
||||
* {@link #getContainerKey()} returns the respective index or key if the parameter's
|
||||
* datatype supports such access.
|
||||
*/
|
||||
@Nullable
|
||||
public Object getContainer() {
|
||||
@@ -83,9 +83,10 @@ public class ParameterErrors extends ParameterValidationResult implements Errors
|
||||
}
|
||||
|
||||
/**
|
||||
* When {@code @Valid} is declared on a {@link List}, this method returns
|
||||
* the index under which the validated object {@link #getArgument() argument}
|
||||
* is stored in the list {@link #getContainer() container}.
|
||||
* When {@code @Valid} is declared on an indexed type, such as {@link List},
|
||||
* this method returns the index under which the validated object
|
||||
* {@link #getArgument() argument} is stored in the list
|
||||
* {@link #getContainer() container}.
|
||||
*/
|
||||
@Nullable
|
||||
public Integer getContainerIndex() {
|
||||
@@ -93,8 +94,8 @@ public class ParameterErrors extends ParameterValidationResult implements Errors
|
||||
}
|
||||
|
||||
/**
|
||||
* When {@code @Valid} is declared on a {@link java.util.Map}, this method
|
||||
* returns the key under which the validated object {@link #getArgument()
|
||||
* When {@code @Valid} is declared on a keyed typed, such as {@link java.util.Map},
|
||||
* this method returns the key under which the validated object {@link #getArgument()
|
||||
* argument} is stored in the map {@link #getContainer()}.
|
||||
*/
|
||||
@Nullable
|
||||
|
||||
Reference in New Issue
Block a user