说我有一个简单的POJO,如下面注释了Jackson 2.1和Hibernate Validator 4.3.1注释:
final public class Person {
@JsonProperty("nm")
@NotNull
final public String name;
public Person(String name) {
this.name = name;
}
}
我将这样的JSON发送到Web服务:
{"name": null}
报告时,Hibernate ConstraintViolation使用类成员标识符“name”而不是JsonProperty注释值。有谁知道是否可以让Hibernate Validator查看类的注释并使用该值代替?
答案 0 :(得分:4)
不幸的是,没有简单的方法可以做到这一点。但是这里有一些可以帮助你的见解:
从ConstraintViolationException
,您可以获得一组ConstraintViolation
,它会公开约束违规上下文:
ConstraintViolation#getLeafBean()
:如果是bean约束,则此方法返回应用约束的bean实例。ConstraintViolation#getPropertyPath()
:返回invalid属性的路径。 从属性路径中,您可以获取叶节点:
Path propertyPath = constraintViolation.getPropertyPath();
Optional<Path.Node> leafNodeOptional =
StreamSupport.stream(propertyPath.spliterator(), false).reduce((a, b) -> b);
然后检查节点的类型是否为PROPERTY
并获取其名称:
String nodeName = null;
if (leafNodeOptional.isPresent()) {
Path.Node leafNode = leafNodeOptional.get();
if (ElementKind.PROPERTY == leafNode.getKind()) {
nodeName = leafNode.getName();
}
}
要从叶子bean类中获取可用的JSON属性,您可以使用Jackson对其进行内省(请参阅此answer和此answer以获取更多详细信息):
Class<?> beanClass = constraintViolation.getLeafBean().getClass();
JavaType javaType = mapper.getTypeFactory().constructType(beanClass);
BeanDescription introspection = mapper.getSerializationConfig().introspect(javaType);
List<BeanPropertyDefinition> properties = introspection.findProperties();
然后通过将叶节点名称与Field
中的BeanPropertyDefinition
名称进行比较来过滤属性:
Optional<String> jsonProperty = properties.stream()
.filter(property -> nodeName.equals(property.getField().getName()))
.map(BeanPropertyDefinition::getName)
.findFirst();
使用JAX-RS(如果您使用它),您可以定义ExceptionMapper
来处理ConstraintViolationException
s:
@Provider
public class ConstraintViolationExceptionMapper
implements ExceptionMapper<ConstraintViolationException> {
@Override
public Response toResponse(ConstraintViolationException exception) {
...
}
}
要使用ObjectMapper
中的ExceptionMapper
,您可以为其提供ContextResolver<T>
:
@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;
}
}
在Providers
中注入ExceptionMapper
界面:
@Context
private Providers providers;
查找ContextResolver<T>
,然后获取ObjectMapper
实例:
ContextResolver<ObjectMapper> resolver =
providers.getContextResolver(ObjectMapper.class, MediaType.WILDCARD_TYPE);
ObjectMapper mapper = resolver.getContext(ObjectMapper.class);
如果您有兴趣获取@XxxParam
名称,请参阅此answer。
答案 1 :(得分:3)
不,那是不可能的。 Hibernate Validator 5(Bean Validation 1.1)具有ParameterNameProvider
s的概念,如果违反了方法参数约束,则返回要报告的名称,但没有可比较的属性约束。
答案 2 :(得分:0)
当我使用 issue 模块进行验证时,我提出了这个 problem-spring-web,并且它不支持开箱即用的 bean 定义名称作为休眠。所以我想出了以下逻辑来覆盖 createViolation
的 ConstraintViolationAdviceTrait
并获取该字段的 JSONProperty
字段名称并再次创建违规。
public class CustomBeanValidationAdviceTrait implements ValidationAdviceTrait {
private final ObjectMapper objectMapper;
public CustomBeanValidationAdviceTrait(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public Violation createViolation(ConstraintViolation violation) {
String propertyName = getPropertyName(violation.getRootBeanClass(), violation.getPropertyPath().toString());
return new Violation(this.formatFieldName(propertyName), violation.getMessage());
}
private String getPropertyName(Class clazz, String defaultName) {
JavaType type = objectMapper.constructType(clazz);
BeanDescription desc = objectMapper.getSerializationConfig().introspect(type);
return desc.findProperties()
.stream()
.filter(prop -> prop.getInternalName().equals(defaultName))
.map(BeanPropertyDefinition::getName)
.findFirst()
.orElse(defaultName);
}