JAXB

Czym jest JAXB

JAXB , czyli Java Architecture for XML Binding, jest specyfikacją wchodzącą w skład Javy SE pozwalającą na manipulację dokumentami XML. Najczęściej wykorzystywana jest w połączeniu ze specyfikacjami definiowania webserwisów takimi jak JAX-WS i JAX-RS.

jaxb

JAXB przypomina nieco frameworki do mapowania obiektowo relacyjnego takie jak Hibernate, ponieważ przy pomocy zestawu predefiniowanych adnotacji jesteśmy w stanie kontrolować wynik marshallingu do dokumentu XML i unmarshallingu do obiektu POJO. W rzeczywistości spotkamy się z takimi aplikacjami, gdzie klasa definiująca encję JPA będzie jednocześnie oznaczona dodatkowymi adnotacjami JAXB.

Adnotacje

JAXB definiuje zestaw adnotacji, które pozwalają na konfigurację mapowania obiektów Javy. Dzięki takiemu podejściu jest to specyfikacja nieinwazyjna i mogąca być zastosowana do już istniejących, zwykłych klas POJO bez ingerencji w ich kod. Dwie najważniejsze adnotacje to:

  • @XmlRootElement - definiuje, że dana klasa może być mapowana na dokument XML
  • @XmlAccessorType - określa, czy dostęp do składowych klasy będzie odbywał się bezpośrednio, czy poprzez metody dostępowe

Oprócz tego znajdziemy zestaw adnotacji pozwalających określić szczegóły mapowania np. @XmlTransient wyłącza dane pole z mapowania dokumentu (analogicznie jak transient przy serializacji), @XmlAttribute mapuje na atrybut XML.

Wszystkie powyższe adnotacje znajdują się w pakiecie javax.xml.bind.annotation.

Przykład

Stwórzmy prosty przykład mapowania obiektu do XML i odwrotnie.

jaxb project

Zaczynamy od stworzenia prostej klasy zgodnej z konwencją JavaBeans pamiętając o jednej ważnej rzeczy - specyfikacja JAXB wymaga obecności bezparametrowego konstruktora.

Product.java

package pl.javastart.model;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlRootElement;
 import javax.xml.bind.annotation.XmlTransient;
 
 @XmlRootElement
 @XmlAccessorType(XmlAccessType.FIELD)
 public class Product {
 	
 	@XmlTransient
 	private int id;
 	private String name;
 	private String producer;
 	private double price;
 	
 	public Product() {}
 	
 	public Product(int id, String name, String producer, double price) {
 		this.id = id;
 		this.name = name;
 		this.producer = producer;
 		this.price = price;
 	}
 
 	public int getId() {
 		return id;
 	}
 	public void setId(int id) {
 		this.id = id;
 	}
 	public String getName() {
 		return name;
 	}
 	public void setName(String name) {
 		this.name = name;
 	}
 	public String getProducer() {
 		return producer;
 	}
 	public void setProducer(String producer) {
 		this.producer = producer;
 	}
 	public double getPrice() {
 		return price;
 	}
 	public void setPrice(double price) {
 		this.price = price;
 	}
 
 	@Override
 	public String toString() {
 		return "Product [id=" + id 
 				+ ", name=" + name 
 				+ ", producer=" + producer
 				+ ", price=" + price + "]";
 	}
 	
 }

Dzięki adnotacjom JAXB będzie mógł zmapować obiekty typu Product na dokumenty XML. Zauważ, że adnotacje dodaliśmy na poziomie pól klasy, ale można je dodać również na poziomie getterów. Adnotacja @XmlTransient sprawi, że wartość id nie zostanie umieszczone w docelowym XMLu.

package pl.javastart.jaxb.marshaller;
 
 import java.io.File;
 
 import javax.xml.bind.JAXBContext;
 import javax.xml.bind.JAXBException;
 import javax.xml.bind.Marshaller;
 
 import pl.javastart.model.Product;
 
 public class MarshallerExample {
 	public static void main(String[] args) throws JAXBException {
 		JAXBContext ctx = JAXBContext.newInstance(Product.class);
 		Marshaller marshaller = ctx.createMarshaller();
 		marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
 		
 		Product product = new Product(1, "Mleko", "Mlekowita", 2.5);
 		marshaller.marshal(product, new File("product.xml"));
 	}
 }

W celu dokonania Marshallingu najpierw tworzymy kontekst JAXB, a następnie obiekt Marshaller. Możemy go dodatkowo skonfigurować - my ustawiamy wartość Marshaller.JAXB_FORMATTED_OUTPUT na true, dzięki czemu uzyskamy efekt "pretty format", czyli kod będzie czytelniejszy. Marshaller posiada wiele przeciążonych wersji metody marshal() więc możemy wynik zapisać zarówno w pliku jak i konsoli, czy dowolnym strumieniu.

Po uruchomieniu programu utworzony zostanie plik product.xml z zawartością:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <product>
     <name>Mleko</name>
     <producer>Mlekowita</producer>
     <price>2.5</price>
 </product>

Spójrzmy jeszcze na odczyt obiektu z pliku:

package pl.javastart.jaxb.unmarshaller;
 
 import java.io.File;
 
 import javax.xml.bind.JAXBContext;
 import javax.xml.bind.JAXBException;
 import javax.xml.bind.Unmarshaller;
 
 import pl.javastart.model.Product;
 
 public class UnmarshallerExample {
 	public static void main(String[] args) throws JAXBException {
 		JAXBContext ctx = JAXBContext.newInstance(Product.class);
 		Unmarshaller unmarshaller = ctx.createUnmarshaller();
 		
 		Product product = (Product) unmarshaller.unmarshal(new File("product.xml"));
 		System.out.println(product);
 	}
 }

Po uruchomieniu zobaczymy w konsoli wynik metody toString():

tostring

Projekt na Github

Dyskusja i komentarze

Masz pytania do tego wpisu? Może chcesz się podzielić spostrzeżeniami? Zapraszamy dyskusji na naszej grupie na Facebooku.