Как я могу эффективно поддерживать вместе проверку JSR-303 и сопоставление JSON Джексона?

При создании веб-службы RESTful с использованием Spring MVC я чувствую, что столкнулся с сложной ситуацией при попытке объединить десериализацию Jackson JSON и проверку bean-компонента JSR-303.

Одна из замечательных особенностей валидации 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;

   ...
}

Моя проблема с этой настройкой заключается в том, что если клиент отправляет строку в качестве значения для "age", весь процесс десериализации Джексона завершится ошибкой, если не будет возможности конкретно указать причину, по которой он не удалось (т.е. мы никогда не дойдем до проверки). Представьте себе эту полезную нагрузку:

{
   "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",
      }
   ]
}

Простым способом решения этой проблемы было бы просто указать поле «возраст» как тип «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 и т. Д., Конечно, также написать адаптеры пользовательского типа для Джексона. И тогда мой класс Person мог бы быть:

public class Person {

   @NotNull
   private StringValue firstname;

   @NotNull
   private StringValue surname;

   @Min(value = 0)
   private IntegerValue age;

   ...
}

Это почти то, что я хочу, но, к сожалению, аннотации JSR-303, такие как @NotNull и @Min, не распознают мои реализации BindableValue.

Итак, может ли кто-нибудь объяснить, как я мог бы решить эту проблему более понятным способом?

13
задан DrewEaster 11 December 2011 в 10:42
поделиться