Hibernate Validator和Jackson:使用@JsonProperty值作为ConstraintViolation PropertyPath?

Phi*_*rdi 6 hibernate-validator jackson bean-validation

假设我有一个简单的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;
  }
}
Run Code Online (Sandbox Code Playgroud)

我将这样的JSON发送到Web服务:

{"name": null}
Run Code Online (Sandbox Code Playgroud)

报告ConstraintViolation时,Hibernate使用类成员标识符"name"而不是JsonProperty注释值.有谁知道是否可以让Hibernate Validator查看类的注释并使用该值代替?

cas*_*lin 5

不幸的是,没有简单的方法可以做到这一点。但是,以下一些见解可以为您提供帮助:

解析约束违规

从中ConstraintViolationException,您可以获得一组ConstraintViolation,它公开了违反约束的上下文:

从属性路径中,您可以获取叶节点:

Path propertyPath = constraintViolation.getPropertyPath();
Optional<Path.Node> leafNodeOptional = 
        StreamSupport.stream(propertyPath.spliterator(), false).reduce((a, b) -> b);
Run Code Online (Sandbox Code Playgroud)

然后检查节点的类型是否为PROPERTY并获取其名称:

String nodeName = null;

if (leafNodeOptional.isPresent()) {
    Path.Node leafNode = leafNodeOptional.get();
    if (ElementKind.PROPERTY == leafNode.getKind()) {
        nodeName = leafNode.getName();
    }
}
Run Code Online (Sandbox Code Playgroud)

与杰克逊一起自省课程

要从叶子bean类中获取可用的JSON属性,可以使用Jackson进行内省(请参阅此答案和此答案以获取更多详细信息):

Class<?> beanClass = constraintViolation.getLeafBean().getClass();
JavaType javaType = mapper.getTypeFactory().constructType(beanClass);

BeanDescription introspection = mapper.getSerializationConfig().introspect(javaType);
List<BeanPropertyDefinition> properties = introspection.findProperties();
Run Code Online (Sandbox Code Playgroud)

然后通过将叶子节点名称与的Field名称进行比较来过滤属性BeanPropertyDefinition

Optional<String> jsonProperty = properties.stream()
        .filter(property -> nodeName.equals(property.getField().getName()))
        .map(BeanPropertyDefinition::getName)
        .findFirst();
Run Code Online (Sandbox Code Playgroud)

使用JAX-RS?

使用JAX-RS(如果正在使用它),可以定义一个ExceptionMapper来处理ConstraintViolationExceptions:

@Provider
public class ConstraintViolationExceptionMapper 
                 implements ExceptionMapper<ConstraintViolationException> {

    @Override
    public Response toResponse(ConstraintViolationException exception) {
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

要在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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Providers接口注入您的ExceptionMapper

@Context
private Providers providers;
Run Code Online (Sandbox Code Playgroud)

查找您ContextResolver<T>ObjectMapper实例,然后获取实例:

ContextResolver<ObjectMapper> resolver = 
        providers.getContextResolver(ObjectMapper.class, MediaType.WILDCARD_TYPE);
ObjectMapper mapper = resolver.getContext(ObjectMapper.class);
Run Code Online (Sandbox Code Playgroud)

如果您有兴趣获取@XxxParam姓名,请参考此答案

  • @Brice 这是真的。它可以_不用_依赖`ParameterNameProvider`来解决。检查我的 [`ConstraintViolationExceptionMapper`](https://github.com/cassiomolin/tasks-rest-api/blob/master/src/main/java/com/cassiomolin/example/common/api/exceptionmapper/ConstraintViolationExceptionMapper.java)在 GitHub 上。欢迎改进:) (2认同)

Gun*_*nar 3

不,那不可能。Hibernate Validator 5(Bean Validation 1.1)具有 s 的概念ParameterNameProvider,它在违反方法参数约束的情况下返回要报告的名称,但对于属性约束没有可比性。

  • 我已提交问题 [HV-823](https://hibernate.atlassian.net/browse/HV-823) 以支持 Hibernate Validator 中的此用例。 (3认同)