Java Language
JAXB
Ricerca…
introduzione
JAXB o Java Architecture per XML Binding (JAXB) è un framework software che consente agli sviluppatori Java di associare classi Java a rappresentazioni XML. Questa pagina introdurrà i lettori a JAXB usando esempi dettagliati sulle sue funzioni fornite principalmente per il marshalling e l'un-marshaling di oggetti Java nel formato xml e viceversa.
Sintassi
JAXB.marshall (oggetto, fileObjOfXML);
Object obj = JAXB.unmarshall (fileObjOfXML, className);
Parametri
Parametro | Dettagli |
---|---|
fileObjOfXML | Oggetto File di un file XML |
nome della classe | Nome di una classe con estensione .class |
Osservazioni
Utilizzando lo strumento XJC disponibile nel JDK, il codice java per una struttura xml descritta in uno schema xml (file .xsd
) può essere generato automaticamente, vedere l' argomento XJC .
Scrivere un file XML (eseguire il marshalling di un oggetto)
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class User {
private long userID;
private String name;
// getters and setters
}
Usando l'annotazione XMLRootElement
, possiamo contrassegnare una classe come elemento radice di un file XML.
import java.io.File;
import javax.xml.bind.JAXB;
public class XMLCreator {
public static void main(String[] args) {
User user = new User();
user.setName("Jon Skeet");
user.setUserID(8884321);
try {
JAXB.marshal(user, new File("UserDetails.xml"));
} catch (Exception e) {
System.err.println("Exception occurred while writing in XML!");
} finally {
System.out.println("XML created");
}
}
}
marshal()
è usato per scrivere il contenuto dell'oggetto in un file XML. Qui user
oggetto user
e un nuovo oggetto File
vengono passati come argomenti al marshal()
.
In caso di esecuzione riuscita, viene creato un file XML denominato UserDetails.xml
nel percorso classe con il contenuto sottostante.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<user>
<name>Jon Skeet</name>
<userID>8884321</userID>
</user>
Lettura di un file XML (annullamento della memoria)
Leggere un file XML denominato UserDetails.xml
con il contenuto sottostante
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<user>
<name>Jon Skeet</name>
<userID>8884321</userID>
</user>
Abbiamo bisogno di una classe POJO denominata User.java
come di seguito
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class User {
private long userID;
private String name;
// getters and setters
}
Qui abbiamo creato le variabili e il nome della classe in base ai nodi XML. Per XmlRootElement
, usiamo l'annotazione XmlRootElement
sulla classe.
public class XMLReader {
public static void main(String[] args) {
try {
User user = JAXB.unmarshal(new File("UserDetails.xml"), User.class);
System.out.println(user.getName()); // prints Jon Skeet
System.out.println(user.getUserID()); // prints 8884321
} catch (Exception e) {
System.err.println("Exception occurred while reading the XML!");
}
}
}
Qui viene usato il metodo unmarshal()
per analizzare il file XML. Prende il nome del file XML e il tipo di classe come due argomenti. Quindi possiamo usare i metodi getter dell'oggetto per stampare i dati.
Utilizzando XmlAdapter per generare il formato xml desiderato
Quando il formato XML desiderato differisce dal modello a oggetti Java, è possibile utilizzare un'implementazione XmlAdapter per trasformare l'oggetto modello in oggetto formato xml e viceversa. Questo esempio mostra come inserire il valore di un campo in un attributo di un elemento con il nome del campo.
public class XmlAdapterExample {
@XmlAccessorType(XmlAccessType.FIELD)
public static class NodeValueElement {
@XmlAttribute(name="attrValue")
String value;
public NodeValueElement() {
}
public NodeValueElement(String value) {
super();
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
public static class ValueAsAttrXmlAdapter extends XmlAdapter<NodeValueElement, String> {
@Override
public NodeValueElement marshal(String v) throws Exception {
return new NodeValueElement(v);
}
@Override
public String unmarshal(NodeValueElement v) throws Exception {
if (v==null) return "";
return v.getValue();
}
}
@XmlRootElement(name="DataObject")
@XmlAccessorType(XmlAccessType.FIELD)
public static class DataObject {
String elementWithValue;
@XmlJavaTypeAdapter(value=ValueAsAttrXmlAdapter.class)
String elementWithAttribute;
}
public static void main(String[] args) {
DataObject data = new DataObject();
data.elementWithValue="value1";
data.elementWithAttribute ="value2";
ByteArrayOutputStream baos = new ByteArrayOutputStream();
JAXB.marshal(data, baos);
String xmlString = new String(baos.toByteArray(), StandardCharsets.UTF_8);
System.out.println(xmlString);
}
}
Configurazione automatica del mapping XML di campi / proprietà (@XmlAccessorType)
Annotation @XmlAccessorType
determina se i campi / proprietà verranno serializzati automaticamente in XML. Nota: le annotazioni @XmlElement
campi e sui metodi @XmlElement
, @XmlAttribute
o @XmlTransient
hanno la precedenza sulle impostazioni predefinite.
public class XmlAccessTypeExample {
@XmlAccessorType(XmlAccessType.FIELD)
static class AccessorExampleField {
public String field="value1";
public String getGetter() {
return "getter";
}
public void setGetter(String value) {}
}
@XmlAccessorType(XmlAccessType.NONE)
static class AccessorExampleNone {
public String field="value1";
public String getGetter() {
return "getter";
}
public void setGetter(String value) {}
}
@XmlAccessorType(XmlAccessType.PROPERTY)
static class AccessorExampleProperty {
public String field="value1";
public String getGetter() {
return "getter";
}
public void setGetter(String value) {}
}
@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
static class AccessorExamplePublic {
public String field="value1";
public String getGetter() {
return "getter";
}
public void setGetter(String value) {}
}
public static void main(String[] args) {
try {
System.out.println("\nField:");
JAXB.marshal(new AccessorExampleField(), System.out);
System.out.println("\nNone:");
JAXB.marshal(new AccessorExampleNone(), System.out);
System.out.println("\nProperty:");
JAXB.marshal(new AccessorExampleProperty(), System.out);
System.out.println("\nPublic:");
JAXB.marshal(new AccessorExamplePublic(), System.out);
} catch (Exception e) {
System.err.println("Exception occurred while writing in XML!");
}
}
} // outer class end
Produzione
Field:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<accessorExampleField>
<field>value1</field>
</accessorExampleField>
None:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<accessorExampleNone/>
Property:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<accessorExampleProperty>
<getter>getter</getter>
</accessorExampleProperty>
Public:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<accessorExamplePublic>
<field>value1</field>
<getter>getter</getter>
</accessorExamplePublic>
Configurazione del mapping XML di campo / proprietà manuale
Annotazioni @XmlElement
, @XmlAttribute
o @XmlTransient
e altro nel pacchetto javax.xml.bind.annotation
consentono al programmatore di specificare quale e in che modo i campi o le proprietà marcati devono essere serializzati.
@XmlAccessorType(XmlAccessType.NONE) // we want no automatic field/property marshalling
public class ManualXmlElementsExample {
@XmlElement
private String field="field value";
@XmlAttribute
private String attribute="attr value";
@XmlAttribute(name="differentAttribute")
private String oneAttribute="other attr value";
@XmlElement(name="different name")
private String oneName="different name value";
@XmlTransient
private String transientField = "will not get serialized ever";
@XmlElement
public String getModifiedTransientValue() {
return transientField.replace(" ever", ", unless in a getter");
}
public void setModifiedTransientValue(String val) {} // empty on purpose
public static void main(String[] args) {
try {
JAXB.marshal(new ManualXmlElementsExample(), System.out);
} catch (Exception e) {
System.err.println("Exception occurred while writing in XML!");
}
}
}
Specifica di un'istanza XmlAdapter per (ri) utilizzare i dati esistenti
A volte devono essere utilizzate specifiche istanze di dati. La ricreazione non è desiderata e il riferimento a dati static
avrebbe un odore di codice.
È possibile specificare un XmlAdapter
esempio Unmarshaller
dovrebbe usare, che permette all'utente di utilizzare XmlAdapter
s senza costruttore a zero arg e / o trasmettere dati alla scheda.
Esempio
Classe utente
La seguente classe contiene un nome e un'immagine dell'utente.
import java.awt.image.BufferedImage;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlRootElement
public class User {
private String name;
private BufferedImage image;
@XmlAttribute
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@XmlJavaTypeAdapter(value=ImageCacheAdapter.class)
@XmlAttribute
public BufferedImage getImage() {
return image;
}
public void setImage(BufferedImage image) {
this.image = image;
}
public User(String name, BufferedImage image) {
this.name = name;
this.image = image;
}
public User() {
this("", null);
}
}
Adattatore
Per evitare di creare la stessa immagine in memoria due volte (oltre a scaricare nuovamente i dati), l'adattatore memorizza le immagini in una mappa.
Per codice Java 7 valido sostituire il metodo getImage
con
public BufferedImage getImage(URL url) {
BufferedImage image = imageCache.get(url);
if (image == null) {
try {
image = ImageIO.read(url);
} catch (IOException ex) {
Logger.getLogger(ImageCacheAdapter.class.getName()).log(Level.SEVERE, null, ex);
return null;
}
imageCache.put(url, image);
reverseIndex.put(image, url);
}
return image;
}
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class ImageCacheAdapter extends XmlAdapter<String, BufferedImage> {
private final Map<URL, BufferedImage> imageCache = new HashMap<>();
private final Map<BufferedImage, URL> reverseIndex = new HashMap<>();
public BufferedImage getImage(URL url) {
// using a single lookup using Java 8 methods
return imageCache.computeIfAbsent(url, s -> {
try {
BufferedImage img = ImageIO.read(s);
reverseIndex.put(img, s);
return img;
} catch (IOException ex) {
Logger.getLogger(ImageCacheAdapter.class.getName()).log(Level.SEVERE, null, ex);
return null;
}
});
}
@Override
public BufferedImage unmarshal(String v) throws Exception {
return getImage(new URL(v));
}
@Override
public String marshal(BufferedImage v) throws Exception {
return reverseIndex.get(v).toExternalForm();
}
}
XML di esempio
I seguenti 2 xmls sono per Jon Skeet e la sua controparte earth 2, che hanno entrambi lo stesso aspetto e quindi usano lo stesso avatar.
<?xml version="1.0" encoding="UTF-8"?>
<user name="Jon Skeet" image="https://www.gravatar.com/avatar/6d8ebb117e8d83d74ea95fbdd0f87e13?s=328&d=identicon&r=PG"/>
<?xml version="1.0" encoding="UTF-8"?>
<user name="Jon Skeet (Earth 2)" image="https://www.gravatar.com/avatar/6d8ebb117e8d83d74ea95fbdd0f87e13?s=328&d=identicon&r=PG"/>
Usando l'adattatore
ImageCacheAdapter adapter = new ImageCacheAdapter();
JAXBContext context = JAXBContext.newInstance(User.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
// specifiy the adapter instance to use for every
// @XmlJavaTypeAdapter(value=ImageCacheAdapter.class)
unmarshaller.setAdapter(ImageCacheAdapter.class, adapter);
User result1 = (User) unmarshaller.unmarshal(Main.class.getResource("user.xml"));
// unmarshal second xml using the same adapter instance
Unmarshaller unmarshaller2 = context.createUnmarshaller();
unmarshaller2.setAdapter(ImageCacheAdapter.class, adapter);
User result2 = (User) unmarshaller2.unmarshal(Main.class.getResource("user2.xml"));
System.out.println(result1.getName());
System.out.println(result2.getName());
// yields true, since image is reused
System.out.println(result1.getImage() == result2.getImage());
Associazione di uno spazio dei nomi XML a una classe Java serializzabile.
Questo è un esempio di un file package-info.java
che associa uno spazio dei nomi XML a una classe Java serializzabile. Questo dovrebbe essere collocato nello stesso pacchetto delle classi Java che dovrebbero essere serializzate usando lo spazio dei nomi.
/**
* A package containing serializable classes.
*/
@XmlSchema
(
xmlns =
{
@XmlNs(prefix = MySerializableClass.NAMESPACE_PREFIX, namespaceURI = MySerializableClass.NAMESPACE)
},
namespace = MySerializableClass.NAMESPACE,
elementFormDefault = XmlNsForm.QUALIFIED
)
package com.test.jaxb;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
Utilizzando XmlAdapter per tagliare la stringa.
package com.example.xml.adapters;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class StringTrimAdapter extends XmlAdapter<String, String> {
@Override
public String unmarshal(String v) throws Exception {
if (v == null)
return null;
return v.trim();
}
@Override
public String marshal(String v) throws Exception {
if (v == null)
return null;
return v.trim();
}
}
E in package-info.java aggiungi la seguente dichiarazione.
@XmlJavaTypeAdapter(value = com.example.xml.adapters.StringTrimAdapter.class, type = String.class)
package com.example.xml.jaxb.bindings;// Packge where you intend to apply trimming filter
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;