Skip to content

Commit

Permalink
Reprot all violiation in collection
Browse files Browse the repository at this point in the history
  • Loading branch information
Ted Liang committed May 11, 2021
1 parent 7ff9d68 commit 285f500
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package org.unbrokendome.jackson.beanvalidation;

import com.fasterxml.jackson.core.JsonStreamContext;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
Expand All @@ -12,25 +15,28 @@
*/
final class InvalidObject {

private final Class<?> type;
private Set<ConstraintViolation<?>> violations;
private int indexInArray;


InvalidObject(Class<?> type, Set<ConstraintViolation<?>> violations) {
this.type = type;
this.violations = new LinkedHashSet<>(violations);
InvalidObject(ConstraintViolationException ex, JsonStreamContext parsingContext) {
this.violations = new LinkedHashSet<>(ex.getConstraintViolations());
this.indexInArray = PropertyPathUtils.getIndexInArray(parsingContext);
}


Class<?> getType() {
return type;
return violations.iterator().next().getRootBeanClass();
}


Set<ConstraintViolation<?>> getViolations() {
return Collections.unmodifiableSet(violations);
}

int getIndexInArray() {
return indexInArray;
}

void addAdditionalViolations(Set<? extends ConstraintViolation<?>> violations) {
this.violations.addAll(violations);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ static Path constructPropertyPath(SettableBeanProperty prop, BeanValidationFeatu
@Nonnull
static Path constructPropertyPath(SettableBeanProperty prop, BeanValidationFeatureSet features,
@Nullable JsonStreamContext parsingContext) {
return constructPropertyPath(prop, features, getIndexInArray(parsingContext));
}

@Nonnull
static Path constructPropertyPath(SettableBeanProperty prop, BeanValidationFeatureSet features, int indexInArray) {

PathBuilder propertyPathBuilder = PathBuilder.create()
.appendBeanNode();
Expand All @@ -39,13 +44,8 @@ static Path constructPropertyPath(SettableBeanProperty prop, BeanValidationFeatu
propertyName = prop.getName();
}

if (parsingContext != null && parsingContext.inArray()) {
if (parsingContext.hasCurrentIndex()) {
propertyName += "[" + parsingContext.getCurrentIndex() + "]";
} else if (parsingContext.getParent().getCurrentValue() instanceof Collection) {
Collection currentValue = (Collection) parsingContext.getParent().getCurrentValue();
propertyName += "[" + currentValue.size() + "]";
}
if (indexInArray >= 0) {
propertyName += "[" + indexInArray + "]";
}

if (prop instanceof CreatorProperty &&
Expand All @@ -62,4 +62,17 @@ static Path constructPropertyPath(SettableBeanProperty prop, BeanValidationFeatu

return propertyPathBuilder.build();
}

static int getIndexInArray(@Nullable JsonStreamContext parsingContext) {
int index = -1;
if (parsingContext != null && parsingContext.inArray()) {
if (parsingContext.hasCurrentIndex()) {
index = parsingContext.getCurrentIndex();
} else if (parsingContext.getParent().getCurrentValue() instanceof Collection) {
Collection currentValue = (Collection) parsingContext.getParent().getCurrentValue();
index = currentValue.size();
}
}
return index;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import javax.validation.constraints.NotNull;
import javax.validation.metadata.ConstraintDescriptor;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
Expand Down Expand Up @@ -483,8 +484,7 @@ protected Object _deserializeUsingPropertyBased(JsonParser p, DeserializationCon
} catch (ConstraintViolationException ex) {
// Retrieve any violation from the set, to find out which concrete type was
// being instantiated
ConstraintViolation<?> violation = ex.getConstraintViolations().iterator().next();
bean = new InvalidObject(violation.getRootBeanClass(), ex.getConstraintViolations());
bean = new InvalidObject(ex, p.getParsingContext());

} catch (Exception e) {
bean = wrapInstantiationProblem(e, ctxt);
Expand All @@ -506,6 +506,10 @@ protected Object _deserializeUsingPropertyBased(JsonParser p, DeserializationCon
}
// or just clean?
bean = deserialize(p, ctxt, bean);

if (p.getParsingContext().inArray()) {
return bean;
}
return unwrapInvalidObject(bean);
}
continue;
Expand Down Expand Up @@ -541,6 +545,12 @@ protected Object _deserializeUsingPropertyBased(JsonParser p, DeserializationCon
Object bean = null;
try {
bean = creator.build(ctxt, buffer);
} catch (ConstraintViolationException e) {
if (p.getParsingContext().inArray()) {
bean = new InvalidObject(e, p.getParsingContext());
} else {
wrapInstantiationProblem(e, ctxt);
}
} catch (Exception e) {
wrapInstantiationProblem(e, ctxt);
assert false; // never gets here
Expand Down Expand Up @@ -581,6 +591,18 @@ protected Object _deserializeProperty(
String beanPropertyName = PropertyUtils.getPropertyNameFromMember(prop.getMember());
propertyViolations =
(Set) validator.validateValue(handledType(), beanPropertyName, value);
} else if (value instanceof Collection) {
propertyViolations = (Set<ConstraintViolation<?>>) ((Collection) value).stream()
.filter(InvalidObject.class::isInstance)
.flatMap(item -> {
InvalidObject invalidObject = (InvalidObject) item;
Path basePath = PropertyPathUtils.constructPropertyPath(
prop, features, invalidObject.getIndexInArray());
return invalidObject.getViolations().stream()
.map(violation -> ConstraintViolationUtils.withBasePath(
violation, bean, (Class) handledType(), basePath));
})
.collect(Collectors.toSet());
}

} catch (MismatchedInputException ex) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,5 +176,17 @@ class KotlinValidationTest : AbstractValidationTest() {
assertThat(violations).hasSize(1)
assertThat(violations).hasViolation<JsonRequired>(violationPath)
}

@Test
fun `should report violation on all records in @Valid-annotated nested list`() {

val json = """{ "nested": [{}, {"value":"2"}, {"value":null}, {"value":"4"}] }"""

val violations = assertViolationsOnDeserialization<ValidatedDataWithValidNestedList>(json)

assertThat(violations).hasSize(2)
assertThat(violations).hasViolation<JsonRequired>("nested[0].value")
assertThat(violations).hasViolation<NotNull>("nested[2].value")
}
}
}

0 comments on commit 285f500

Please sign in to comment.