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.