JAXB
Frissítve: 2017. november 18.
Mostanában, ha XML-t kell használnom Javaból, először mindig a JAXB (Java Architecture for XML Binding) jut eszembe, ami egy nagyon egyszerűen, gyorsan használatba vehető binding framework, melynek feladata a Java objektumok és az XML elemek egymáshoz rendelése, egymással való megfeleltetése. Magasabb absztrakciós szintet képvisel, mint a SAX, vagy a DOM, hiszen itt gyakorlatilag a programból csak Java objektumokkal kell dolgoznunk, az oda és vissza alakítást elvégzi a keretrendszer. Hasonló az ORM-hez, csak ott a feladat Java objektumok relációs adatbázis elemekké (sorokká) alakítása.
A binding és a mapping szavakat általában keverik, nincs is kiforrott definíció, azonban érdemes megfontolnunk Mark D. Hansen SOA Using Web Services című könyében az általa kifejlesztett terminológiát. Szerinte a binding esetén az XML séma egy megvalósulása a belőle képzett Java osztály, míg mapping esetén a Java osztályt például az üzleti logikánkban használjuk, csak egy megjelenési formája, hogy XML-be is mentjük, vagy onnan betöltjük, esetleg adatbázisba perzisztáljuk. Emiatt szerinte a JAXB egy binding framework.
A JAXB egy szabvány (JSR-222,
melynek a referencia implementációja a stílusosan csak JAXB Reference
Implementation-nek hívott projekt, mely már
a JDK része is. A Java 8-ba a JAXB 2.2 verziója került (ez könnyen ellenőrizhető a xjc -version
paranccsal).
Fejlesztésnél választhatjuk azt, hogy a Java kódból indulunk ki, és abból generáljuk le az XML sémát a séma generátor (schema generator) segítségével. Vagy kiindulhatunk az XML sémából, és abból generáljuk le a Java kódot a séma fordító (schema compiler) segítségével. A kettő közötti megfeleltetést Java annotációkkal lehet konfigurálni, amiket az előbbi esetben magunk írunk, az utóbbi esetben a séma fordító generálja le az osztályokba. Szerencsére az előbbi esetben viszonylag kevés annotációt kell használni, hiszen a JAXB is követi az EJB 3 azon törekvését, hogy az annotációknak alapértelmezett értékeik legyenek, amivel már működik a programunk.
A legrosszabb eset, ha XML sémánk is van, és annak megfelelő XML dokumentumot kell gyártani, valamint Java osztályaink is vannak, amik valamilyen üzleti logikát valósítanak meg. Ekkor megpróbálhatunk a JAXB-vel trükközni, hogy úgy helyezzük el az annotációkat a Java osztályokon, hogy pont az az XML jöjjön ki, amit szeretnénk (nehezebb, mert nagy JAXB ismeret kell hozzá), vagy az XML sémából kigeneráljuk a Java osztályokat, mint DTO objektumok, és programból alakítgatjuk a business domaint megvalósítandó osztályainkra (redundánsabb a kód). Tehát a sémának az annotált Java osztályok felelnek meg, míg az XML dokumentumnak a Java objektumok. XML dokumentumból Java objektumok az unmarshall művelettel keletkeznek, fordítva a marshall művelet használható.
Nézzünk is egy egyszerű példát, mely letölthető a
GitHub-ról. Adott egy
könyvkatalógus (Catalog
osztály), és abban könyvek (Book
)
osztály.
A kapcsolat egyirányú, csak a Catalok
hivatkozik a
Book
osztályra.
Ahhoz, hogy XML-lé alakítható legyen, a Catalog
osztályra tegyük rá az
@XmlRootElement
annotációt. A JAXB miatt mindkét osztálynak
rendelkeznie kell paraméter nélküli konstruktorral is.
Az @XmlElement
annotáció hatására a tag neve nem az attribútum neve
lesz, hanem a name
paraméterként megadott.
Ahhoz, hogy ezt XML-be mentsük, a marshall műveletet kell meghívni a
következőképpen. Először egy JAXBContext
-et kell létrehoznunk, és
adjuk meg neki az XML-be menteni kívánt osztályokat. Utána létrehozzuk a
Marshaller
objektumot, majd meghívjuk a marshal metódust.
JAXBContext ctx = JAXBContext.newInstance(Catalog.class, Book.class);
Marshaller marshaller = ctx.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_FRAGMENT,
Boolean.FALSE);
StringWriter writer = new StringWriter();
marshaller.marshal(catalog, writer);
return writer.toString();
És máris megjelenik a kívánt XML.
Ez az alapértelmezett működés, de ezt tetszőleges módon testre szabhatjuk. Például:
- Ha azt akarjuk, hogy az
isbn10
attribútum legyen, tegyük rá agetIsbn10()
metódusra az@XmlAttribute
annotációt - Ha ezt a field-re akarjuk tenni, akkor használjuk osztály szinten a
@XmlAccessorType(XmlAccessType.FIELD)
annotációt, ez jelzi, hogy nem metódusra akarjuk rakni a többi annotációt - Az
@XmlType(propOrder={...})
annotációval adhatjuk meg az elemek sorrendjét - Az
@XmlTransient
annotációval megmondhatjuk egy attribútumra, hogy ne mentődjön az XML-be - Az
@XmlElementWrapper
annotációval az ismétlődő elemek köré tehetünk egy wrapper XML tag-et - Amennyiben névteret is meg akarunk adni, tegyük ezt a
package-info.java
állományban a@XmlSchema
annotáció segítségével.
Ilyenkor az alapértelmezett binding működik, azaz meg van adva, hogy melyik XML séma típushoz milyen Java típus tartozik, és fordítva. Persze ezt is felüldefiniálhatjuk.
Az XML-bel objektumok visszanyerése hasonlóan egyszerű:
A Java forrásból XML sémát a schemagen
paranccsal generálhatunk.
Amennyiben az XML séma felől szeretnénk elindulni, az xjc
parancsot
kell kiadnunk, ami legenerálja a Java osztályokat. Használhatjuk
a JAXB2 Maven Plugin megfelelő goaljait is.