May 17 2008
JAXB 2, zabawy z datą i konwersją typów
Filed under Java,JAXB by Łukasz Dywicki
Jedną z bolączek JAXB jest problematyczna obsługa dat i czasów. Przypomnijmy sobie schemat użyty w jednej z wcześniejszych not.
<!-- Definicja książki --> <complextype name="Book"> <sequence> <element name="title" type="string" minOccurs="1" /> <element name="isbn" type="string" /> <element name="releaseDate" type="date" /> <element name="author" type="tns:Author" maxOccurs="unbounded" /> </sequence> </complextype>
Jak widać, każda książka ma określoną datę wydania. Jakkolwiek typ date z XML Schema nie jest bezpośrednio odwzorowany do java.util.Date czy też java.util.Calendar. W przypadku implementacji dostarczonej przez Sun mamy do czynienia z obiektami klasy XMLGregorianCalendarImpl. Rozwiązaniem problemu jest dodanie odpowiedniego adaptera. Możemy robić to ręcznie ale najwygodniejszym rozwiązaniem będzie skorzystanie z gotowca – klasy javax.xml.bind.DatatypeConverter. Jej użycie wygląda następująco:
< ?xml version="1.0" encoding="UTF-8"?> <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://dywicki.pl/court" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" jaxb:version="2.0" xmlns:tns="http://dywicki.pl/court" elementFormDefault="qualified"> <annotation> <appinfo> <jaxb :globalBindings> <jaxb :javaType name="java.util.Calendar" xmlType="date" parseMethod="javax.xml.bind.DatatypeConverter.parseDate" printMethod="javax.xml.bind.DatatypeConverter.printDate" /> </jaxb:globalBindings> </appinfo> </annotation> <!-- tutaj deklaracje typów --> </schema>
Podobne rozwiązanie można wykorzystać na poziomie pojedyńczego elementu aby zmapować konkretny element do obiektu. Element annotation można również dodać do deklaracji simpleType (powiedzmy reprezentacja numeru pesel + proste restrykcje).
<complextype name="File"> <sequence> <element name="category" type="string"> <annotation> <appinfo> <jaxb :javaType name="pl.dywicki.court.Category" parseMethod="pl.dywicki.court.Category.parse" printMethod="pl.dywicki.court.Category.print" /> </appinfo> </annotation> </element> </sequence> </complextype>
JAXB wygeneruje wówczas odpowiednie adnotacje dla tworzonych klas oraz adaptery, które odwołują się do metod wskazanych w atrybutach elementu javaType.
W moim przypadku kod klasy Category wygląda następująco:
package pl.dywicki.court; /** * Reprezentacja kategorii sprawy. * * @author Lukasz Dywicki <a href="mailto:luke@code-house.net">luke@code-house.net</a> **/ public class Category { private String name; public Category(String value) { this.name = value; } public static Category parse(String value) { return new Category(value); } public static String print(Category value) { return value.name; } }
Jest to oczywiście maksymalnie uproszczony przykład. W konstruktorze bądź metodach, które konwertują string do obiektu możemy podpiąć walidację i bronić się wyjątkami przed nieprawidłowymi wartościami by uniemożliwić deserializację dokumentu.
Widzę że wszystkie rzeczy do zdefiniowania upakowujesz do XML, a może tak chodź raz YAML ? :)
Hej wojtosz!
Kiedyś rozwodziłem się nad tym dlaczego nie yaml. Yaml to tylko konfiguracja, xml to znacznie więcej. :)
Drugi listing jest zwalony – nie kompiluje się, bo:
1.
Jest tag “<jaxb :globalBindings>” , a powinno być “<jaxb:globalBindings>” .
2.
Tag “<jaxb:globalBindings>” , powinien być zamknięty przez “</jaxb:globalBindings>” , a nie przez “</jaxb>”
Hej, tt. Poprawiłem drugi błąd w listingu. Pierwszy wynika z wad w skrypcie do kolorowania składni.