json - 如何有效地同时支持 JSR-303 验证和 Jackson JSON 映射?

标签 json spring-mvc jackson bean-validation

在使用 Spring MVC 构建 RESTful Web 服务时,我觉得在尝试结合 Jackson JSON 反序列化和 JSR-303 bean 验证时遇到了一个棘手的情况。

JSR-303 验证的优点之一是可以返回与目标对象相关的所有验证错误,而不仅仅是在第一次违反约束时失败(尽管如果您想使用它,可以使用快速失败模式)。

想象一个场景,您有一个 JSON 有效负载,例如:

{
   "firstname": "Joe",
   "surname": "Bloggs",
   "age": 25
}

然后你有你想要映射到的对象:
public class Person {

   @NotNull
   private String firstname;

   @NotNull
   private String surname;

   @Min(value = 0)
   private int age;

   ...
}

我对这个设置的问题是,如果客户端发送一个字符串作为“年龄”的值,整个 jackson 反序列化过程将失败,而无法具体定位失败的原因(即我们永远无法验证)。那么,想象一下这个有效载荷:
{
   "age": "invalid"
}

在这种情况下,我想返回的是“错误”响应,例如:
{ 
   "errors" : [
      "error": {
         "field": "firstname",
         "message": "Must not be null",
      },
      "error": {
         "field": "surname",
         "message": "Must not be null",
      },
      "error": {
         "field": "age",
         "message": "Must be numeric value greater than or equal 0",
      }
   ]
}

解决这个问题的简单方法是简单地将“age”字段指定为“String”类型,然后在将其传递到持久层时转换该值。但是,我不太喜欢以一揽子方式求助于使用字符串的数据传输对象的想法——就保持简洁的设计而言,这样做似乎是错误的。

我想到的另一个选择是创建一个类,例如:
public abstract class BindableValue<T> {

   private String stringValue;

   private T value;

   public T getValue() {
      if(value == null) {
         value = fromString(stringValue);
      }
      return value;
   }

   protected abstract T fromString(String stringValue);
}

然后我可以创建 IntegerValue、LongValue、DateTimeValue 等实现,当然也可以为 Jackson 编写自定义类型适配器。然后我的 Person 类可能是:
public class Person {

   @NotNull
   private StringValue firstname;

   @NotNull
   private StringValue surname;

   @Min(value = 0)
   private IntegerValue age;

   ...
}

这几乎是我想要的,但不幸的是,@NotNull 和 @Min 等 JSR-303 注释无法识别我的 BindableValue 实现。

那么,任何人都可以解释一种我可以以更清洁的方式解决这个问题的方法吗?

最佳答案

我现在意识到做到这一点的最好方法可能是简单地对所有字段使用字符串,但封装这一事实并仍然提供特定于类型的方法 setter 和 getter 。例如

public class Person {

    @NotNull
    private String name;

    @NotNull
    @Min(value = 0)
    private String age;

    public String getName() {
        return name;
    }

    public Integer getAge() {
        return age != null ? new Integer(age) : null;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age == null ? null : String.valueOf(age);
    }

    @JsonSetter
    private void setAge(String age) {
        this.age = age;
    }
 }

这样,我得到了我所追求的行为,同时确保公共(public)对象接口(interface)仍然是“干净的”。

关于json - 如何有效地同时支持 JSR-303 验证和 Jackson JSON 映射?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8463356/

相关文章:

Android - 使用 Retrofit 解析没有数组标题的 JSON

java - Spring数据ldap配置

java - 非 spring web 应用程序是否可以并行 Spring-MVC 应用程序?

java - 我想使用 java 和 angularjs 显示数据列表

java - 使用@JsonFormat 的 jackson 日期格式?

java - 无法反序列化,原因未知(MismatchedInputException)

javascript - python 的 json.dumps 输出对 javascript 安全吗?

mysql - JSON_SET 不更新 MySQL 中的空 JSON 字段

java - jackson 解析javascript函数

Python Json加载()返回字符串而不是字典?