Java Language
JAXB
Suche…
Einführung
JAXB oder Java Architecture for XML Binding (JAXB) ist ein Software-Framework, mit dem Java-Entwickler Java-Klassen XML-Repräsentationen zuordnen können. Auf dieser Seite werden die Leser in JAXB anhand von ausführlichen Beispielen zu ihren Funktionen eingeführt, die hauptsächlich für das Marshalling und das Marshaling von Java-Objekten im XML-Format und umgekehrt dienen.
Syntax
JAXB.marshall (object, fileObjOfXML);
Objekt obj = JAXB.unmarshall (fileObjOfXML, Klassenname);
Parameter
Parameter | Einzelheiten |
---|---|
fileObjOfXML | File einer XML-Datei |
Klassenname | Name einer Klasse mit der Erweiterung .class |
Bemerkungen
Mit dem im JDK verfügbaren XJC-Tool kann Java-Code für eine in einem XML-Schema ( .xsd
Datei) beschriebene XML-Struktur automatisch generiert werden. Siehe XJC-Thema .
Schreiben einer XML-Datei (Marshalling eines Objekts)
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class User {
private long userID;
private String name;
// getters and setters
}
Mit der Annotation XMLRootElement
können wir eine Klasse als Wurzelelement einer XML-Datei markieren.
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()
wird verwendet, um den Inhalt des Objekts in eine XML-Datei zu schreiben. Hier werden das user
und ein neues File
Objekt als Argumente an den marshal()
.
Bei erfolgreicher Ausführung wird eine XML-Datei mit dem Namen UserDetails.xml
im Klassenpfad mit dem folgenden Inhalt erstellt.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<user>
<name>Jon Skeet</name>
<userID>8884321</userID>
</user>
Lesen einer XML-Datei (unmarshalling)
So lesen Sie eine XML-Datei mit dem Namen UserDetails.xml
mit dem folgenden Inhalt
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<user>
<name>Jon Skeet</name>
<userID>8884321</userID>
</user>
Wir benötigen eine POJO-Klasse mit dem Namen User.java
( User.java
unten)
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class User {
private long userID;
private String name;
// getters and setters
}
Hier haben wir die Variablen und den Klassennamen gemäß den XML-Knoten erstellt. Um sie zuzuordnen, verwenden wir die Annotation XmlRootElement
für die Klasse.
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!");
}
}
}
Hier wird die unmarshal()
-Methode verwendet, um die XML-Datei zu analysieren. Der XML-Dateiname und der Klassentyp werden als zwei Argumente verwendet. Dann können wir die Getter-Methoden des Objekts verwenden, um die Daten zu drucken.
Verwenden von XmlAdapter zum Generieren des gewünschten XML-Formats
Wenn das gewünschte XML-Format vom Java-Objektmodell abweicht, kann eine XmlAdapter-Implementierung verwendet werden, um das Modellobjekt in ein Objekt im XML-Format und umgekehrt zu transformieren. In diesem Beispiel wird veranschaulicht, wie ein Feldwert in ein Attribut eines Elements mit dem Feldnamen eingefügt wird.
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);
}
}
XML-Mapping-Konfiguration für automatisches Feld / Eigenschaft (@XmlAccessorType)
Die Anmerkung @XmlAccessorType
legt fest, ob Felder / Eigenschaften automatisch in XML serialisiert werden. Beachten Sie, dass die Feld- und Methodenanmerkungen @XmlElement
, @XmlAttribute
oder @XmlTransient
den Standardeinstellungen @XmlTransient
.
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
Ausgabe
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>
Manuelle Konfiguration von Feld- / Eigenschafts-XML-Mappings
Die Anmerkungen @XmlElement
, @XmlAttribute
oder @XmlTransient
und andere im Paket javax.xml.bind.annotation
ermöglichen dem Programmierer, anzugeben, welche und wie markierte Felder oder Eigenschaften serialisiert werden sollen.
@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!");
}
}
}
Angeben einer XmlAdapter-Instanz, um vorhandene Daten (erneut) zu verwenden
Manchmal sollten bestimmte Dateninstanzen verwendet werden. Eine Wiederherstellung ist nicht erwünscht und das Referenzieren static
Daten hätte einen Codegeruch.
Es ist möglich, eine XmlAdapter
Instanz Unmarshaller
, die der Unmarshaller
soll. Unmarshaller
kann der Benutzer XmlAdapter
ohne Zero- XmlAdapter
-Konstruktor verwenden und / oder Daten an den Adapter übergeben.
Beispiel
Benutzerklasse
Die folgende Klasse enthält einen Namen und ein Benutzerbild.
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);
}
}
Adapter
Um zu vermeiden, dass dasselbe Bild zweimal im Speicher erstellt wird (und die Daten erneut heruntergeladen werden), speichert der Adapter die Bilder in einer Karte.
Ersetzen getImage
für gültigen Java 7-Code die getImage
Methode durch
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();
}
}
Beispiel-XMLs
Die folgenden 2 xmls sind für Jon Skeet und sein Gegenstück zu Earth 2, die beide genau gleich aussehen und daher den gleichen Avatar verwenden.
<?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"/>
Verwendung des Adapters
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());
Binden eines XML-Namespaces an eine serialisierbare Java-Klasse.
Dies ist ein Beispiel für eine package-info.java
Datei, die einen XML-Namespace an eine serialisierbare Java-Klasse bindet. Diese sollte in demselben Paket wie die Java-Klassen abgelegt werden, die mit dem Namespace serialisiert werden sollen.
/**
* 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;
Zeichenkette mit XmlAdapter trimmen.
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();
}
}
Fügen Sie in package-info.java folgende Deklaration hinzu.
@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;