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.
1 2 3 4 5 6 7 8 9 | <!-- 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | < ? xml version = "1.0" encoding = "UTF-8" ?> < 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).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | < 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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.