09 октября 2008

Spring MVC. Преобразование данных

Spring MVC: Failed to convert property value of type [java.lang.String] to required type ...

Вот такую ошибку вы получите если захотите создать html форму с текстовым полем , а пожелаете сохранить этот текст, в класс Date (например).

Сделать это можно с помощью следующего кода
Контроллер:

//...
setupFormWithDate(ModelMap model){
java.util.Date nowDate = new Date(System.currentTimeMillis());
model.addAttribute("someDate",nowDate);
return "form";
}
//...

Представление:

<!-- ... -->
<form:input path="${someDate}" />
<!-- ... -->


Решается такая проблема довольно просто. При инициализации необходимо рассказать спрингу как реагировать на поля в которых мы вводим данные типа дата.

@InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
}

В SpringFramework существует специальный класс CustomDateEditor , в котором
реализована логика преобразования нашего объекта типа Date в строку и наоборот. Первый параметр в конструкторе CustomDateEditor формат представления даты как строки, в нешем случае год-месяц-день. Этот класс позаботиться что-бы nowDate из первого примера, преобразовался в
указанный формат и вывелся на форму, при отправке формі на сервер, текстовое значение поля someDate будет также обрабатывать класс CustomDateEditor и преобразует его в правильную дату.
Второй параметр определяет допускаются ли пустые значения в этом поле.

Более сложный вариант, когда мы хотим вывести в текстовое (либо другое) поле значение, которое не принадлежит к классу созданному нами.

Например у нас есть класс Address:
class Address {
int zip;
String country;
String region;
String city;
String street;
String house;
int floor;
int flat;

public String toString(){
return ""+zip+":"+country+":"+region // + ...
}
}
и есть класс User у которого есть домашний и рабочий адреса. Предположим, что
выделять 16 полей на форме под адресса мы не хотим, потому что они будут занимать слишком много места.
Не проблема, создадим 2 поля, назовём их workAddress и homeAddress.
Теперь нам нужен некий Custom Editor, который будет знать как преобразовывать из тектового значения в класс Address и наоборот.
Так как стандартного такого для нашего класса нет и не будет, то создадим свой. Этот класс должен расширять java.beans.PropertyEditorSupport и перегрузить один из его методов , а именно : setAsText. Назовём наш редактор : AddressPropertyEditor.
P.s. обратите внимание, в классе Address я уже создал стандартный метод (toString) , который преобразует объект Address в строку, где все члены разделены двоеточием. Но это же можно было сделать и в классе AddressPropertyEditor, с помощью перегрузки метода getAsText.

Наш редактор свойств будет выглядеть приблизительно так:
class AddressPropertyEditor extends PropertyEditorSupport {

@Override
public void setAsText(String text) {
Address address;
// начало логики преобразования из строки в Address
// TODO text.split(':')
// check if text is correct , throw exceptions if it's not
// TODO address set properies
// конец логики преобразования из строки в Address
setValue(address); // устнавливаем значения типа Address
}

Остался последний штрих. По аналогии с регистрацией CustomDateEditor, надо
зарегистировать редактор для преобразования нашего адресса:

@InitBinder
//...
binder.registerCustomEditor(Address.class, new AddressPropertyEditor());

P.s. Пример с адресом я выбрал конечно яркий, но не реальный.
Другой хороший пример(который я только что придумал).
В одно текстовое поле добавляется List<String> - список текстовых значений. Например список тегов к сообщению в блоге вводиться именно таким образом.

Комментариев нет: