Jakiś czas temu pisałem o dodawaniu adapterów do atrybutów poprzez namespace jaxb, a konkretnie korzystając z elementu jaxb:javaType. Zmiana ta umożliwia eleganckie korzystanie z Map’y po stronie javy (nawet silnie typizowanej z moim hakiem) a także zapis do xml’a string’ów pałkowo-średnikowych. Jednak jak wiadomo taki string to dość toporna struktura danych i jego utrzymanie dość szybko napotkało swoje naturalne ograniczenia:
- po pierwsze rozmiar – po dodaniu paru elementów string pałkowo-średnikowy przestaje być czytelny
- po drugie kontrola wersji – zmiana choćby jednego znaku w stringu pałkowo-średnikowym widoczna jest podczas sprawdzania i checkinowania zmian jako zmiana w jednej długaśnej linii, więc jakakolwiek kontrola tego co zostało zmienione staje się bardzo trudna, no i historia w kontorli wersji na mało się przydaje, no chyba, że ktoś ma wbudowany parser. Ja akurat takiego nie mam, więc każda zmiana to droga przez mękę.
Jak widać problem nabrał sporej rangi, no i pojawiło się pytanie czy nie dałoby się takiego rozwiązania zastąpić czymś bardziej czytelnym, czymś co oddawałoby strukture bez potrzeby łapania dziwnej zwiechy przy kolejnym zetknieciu z tym rozwiązaniem (zwiecha częściowo poświęcona na zadume i ciepłe słowa dla autora a częściowo na sprawdzenie czy czasem się nie cierpi na jakąś chorobę oczu).
Pierwsze co przychodzi do głowy to zaaplikowanie kolejnego adaptera tym razem zamiast do atrybutu to do samego elementu. Pierwsze podejście to próba wdrożenia rozwiązania przedstawionego przez Koshuke Kawaguchi już zresztą parę lat temu , wyglada na bułkę z masełkiem… Niestety jakoś nie udało mi się go skłonić do współpracy. Później wymyśliłem, żeby zamiast przekładania Listy zamienić ją na zwykłą tablicę, bo może dlatego adapter nie chwyta – niestety, tym razem już brak wyjątków, ale lista jest pusta. Podejście trzecie – wstawienie interfejsu kończy się wyjątkiem, bo JAXB nie obsługuje interfejsów. Aż tu jak już zrezygnowany ot tak zacząłem sobie szukać czy ktoś cokolwiek wymyślił w tej kwestii, natrafiłem na zbiór (działąjących!) tutoriali dla jee5 a w nich cały dział poświęcony JAXB. Niestety interesujący mnie przykład zwiera tylko klasy z adnotacjami, bez schemy. Postanowiłem troche poszukać, bo może udałoby się znaleźć jakis inny, który aplikuje adaptera do elementu, wtedy po połączeniu obu możnaby wygenerować całość ze schemy. No i faktycznie, znalazłem to czego potrzebuję.
Ostateczny wynik to:
- dodanie elementu jaxb:Type jako podelementu (…) bez nadrzędnego jaxb:property
- dodanie klasy zawijającej Map’ę czy też inna kolekcję (tak, żeby JAXB mogło operować na konkretnym typie)
- dorzucenie adaptera
- odpalenie JXC
A schema wyglada tak:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc" jxb:extensionBindingPrefixes="xjc" jxb:version="2.0" targetNamespace="http://bedkowski.pl/elementAdapter" xmlns="http://bedkowski.pl/elementAdapter"> <xs:annotation> <xs:appinfo> <jxb:schemaBindings> <jxb:package name="bla" /> </jxb:schemaBindings> </xs:appinfo> </xs:annotation> <xs:element name="kitchenWorldBasket" type="KitchenWorldBasketType" /> <xs:element name="purchaseList" type="PurchaseListType"> </xs:element> <xs:complexType name="KitchenWorldBasketType"> <xs:sequence> <xs:element name="basket" minOccurs="0"> <xs:simpleType> <xs:annotation> <xs:appinfo> <xjc:javaType name="java.util.Map" adapter="bla.AdapterPurchaseListToHashMap" /> </xs:appinfo> </xs:annotation> <xs:restriction base="xs:positiveInteger"> <xs:maxExclusive value="100" /> </xs:restriction> </xs:simpleType> </xs:element> </xs:sequence> </xs:complexType> <xs:complexType name="PurchaseListType"> <xs:sequence> <xs:element name="entry" type="partEntry" nillable="true" minOccurs="0" maxOccurs="unbounded" /> </xs:sequence> </xs:complexType> <xs:complexType name="partEntry"> <xs:simpleContent> <xs:extension base="xs:string"> <xs:attribute name="key" type="xs:int" use="required" /> </xs:extension> </xs:simpleContent> </xs:complexType> </xs:schema>