Unfortunately, there is no easy way to do this. But here are some ideas that might help you:
Violations with tolerance restrictions
From a ConstraintViolationException
you can get a set of ConstraintViolation
that exposes the context of a constraint violation:
From the properties path, you can get the node sheet:
Path propertyPath = constraintViolation.getPropertyPath(); Optional<Path.Node> leafNodeOptional = StreamSupport.stream(propertyPath.spliterator(), false).reduce((a, b) -> b);
Then check if the node type is PROPERTY
and get its name:
String nodeName = null; if (leafNodeOptional.isPresent()) { Path.Node leafNode = leafNodeOptional.get(); if (ElementKind.PROPERTY == leafNode.getKind()) { nodeName = leafNode.getName(); } }
Introspection of a class using Jackson
To get the available JSON properties from the bean class, you can view it with Jackson (see this and answer for more information):
Class<?> beanClass = constraintViolation.getLeafBean().getClass(); JavaType javaType = mapper.getTypeFactory().constructType(beanClass); BeanDescription introspection = mapper.getSerializationConfig().introspect(javaType); List<BeanPropertyDefinition> properties = introspection.findProperties();
Then filter the properties by comparing the name of the leaf node with the name Field
of BeanPropertyDefinition
:
Optional<String> jsonProperty = properties.stream() .filter(property -> nodeName.equals(property.getField().getName())) .map(BeanPropertyDefinition::getName) .findFirst();
Using JAX-RS?
Using JAX-RS (if you use it), you can define an ExceptionMapper
to handle ConstraintViolationException
s:
@Provider public class ConstraintViolationExceptionMapper implements ExceptionMapper<ConstraintViolationException> { @Override public Response toResponse(ConstraintViolationException exception) { ... } }
To use ObjectMapper
in ExceptionMapper
, you can provide a ContextResolver<T>
for it:
@Provider public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> { private final ObjectMapper mapper; public ObjectMapperContextResolver() { mapper = createObjectMapper(); } @Override public ObjectMapper getContext(Class<?> type) { return mapper; } private ObjectMapper createObjectMapper() { ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationFeature.INDENT_OUTPUT, true); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); return mapper; } }
Add the Providers
interface to ExceptionMapper
:
@Context private Providers providers;
Find the ContextResolver<T>
, and then get the ObjectMapper
instance:
ContextResolver<ObjectMapper> resolver = providers.getContextResolver(ObjectMapper.class, MediaType.WILDCARD_TYPE); ObjectMapper mapper = resolver.getContext(ObjectMapper.class);
If you are interested in getting @XxxParam
names, refer to this.