Java Language
JAXB
Recherche…
Introduction
JAXB ou Java Architecture for XML Binding (JAXB) est une infrastructure logicielle permettant aux développeurs Java de mapper des classes Java avec des représentations XML. Cette page présentera les lecteurs à JAXB en utilisant des exemples détaillés de ses fonctions fournies principalement pour le marshaling et le non marshaling des objets Java au format xml et vice-versa.
Syntaxe
JAXB.marshall (objet, fileObjOfXML);
Objet obj = JAXB.unmarshall (fileObjOfXML, className);
Paramètres
Paramètre | Détails |
---|---|
fileObjOfXML | Objet File d'un fichier XML |
nom du cours | Nom d'une classe avec l'extension .class |
Remarques
À l'aide de l'outil XJC disponible dans le JDK, le code Java d'une structure xml décrit dans un schéma xml (fichier .xsd
) peut être généré automatiquement, voir la rubrique XJC .
Ecriture d'un fichier XML (regroupement d'un objet)
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class User {
private long userID;
private String name;
// getters and setters
}
En utilisant l'annotation XMLRootElement
, nous pouvons marquer une classe comme élément racine d'un fichier 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()
est utilisé pour écrire le contenu de l'objet dans un fichier XML. Ici, user
objet user
et un nouvel objet File
sont transmis en tant qu'arguments au marshal()
.
En cas d'exécution réussie, cela crée un fichier XML nommé UserDetails.xml
dans le chemin de classe avec le contenu ci-dessous.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<user>
<name>Jon Skeet</name>
<userID>8884321</userID>
</user>
Lecture d'un fichier XML (désarchivage)
Pour lire un fichier XML nommé UserDetails.xml
avec le contenu ci-dessous
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<user>
<name>Jon Skeet</name>
<userID>8884321</userID>
</user>
Nous avons besoin d'une classe POJO nommée User.java
comme ci-dessous
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class User {
private long userID;
private String name;
// getters and setters
}
Ici, nous avons créé les variables et le nom de la classe en fonction des nœuds XML. Pour les mapper, nous utilisons l'annotation XmlRootElement
sur la 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!");
}
}
}
Ici, la méthode unmarshal()
est utilisée pour analyser le fichier XML. Il prend le nom de fichier XML et le type de classe comme deux arguments. Ensuite, nous pouvons utiliser les méthodes getter de l'objet pour imprimer les données.
Utilisation de XmlAdapter pour générer le format xml souhaité
Lorsque le format XML souhaité diffère du modèle d'objet Java, une implémentation XmlAdapter peut être utilisée pour transformer un objet de modèle en objet au format xml et inversement. Cet exemple montre comment mettre la valeur d'un champ dans un attribut d'un élément avec le nom du champ.
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);
}
}
Configuration automatique du mappage de champs / propriétés (@XmlAccessorType)
Annotation @XmlAccessorType
détermine si les champs / propriétés seront automatiquement sérialisés en XML. Notez que les annotations de champ et de méthode @XmlElement
, @XmlAttribute
ou @XmlTransient
ont priorité sur les paramètres par défaut.
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
Sortie
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>
Configuration manuelle du champ / propriété XML
Les annotations @XmlElement
, @XmlAttribute
ou @XmlTransient
et autres dans le package javax.xml.bind.annotation
permettent au programmeur de spécifier comment et comment les champs ou propriétés marqués doivent être sérialisés.
@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!");
}
}
}
Spécification d'une instance XmlAdapter pour (ré) utiliser des données existantes
Parfois, des instances spécifiques de données doivent être utilisées. La récréation n'est pas souhaitée et le référencement static
données static
aurait une odeur de code.
Il est possible de spécifier un XmlAdapter
exemple , le Unmarshaller
doit utiliser, ce qui permet à l'utilisateur d'utiliser XmlAdapter
s sans constructeur zéro arg et / ou transmettre des données à l'adaptateur.
Exemple
Classe d'utilisateurs
La classe suivante contient un nom et une image d'utilisateur.
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);
}
}
Adaptateur
Pour éviter de créer la même image en mémoire deux fois (ainsi que de télécharger à nouveau les données), l'adaptateur stocke les images dans une carte.
Pour un code Java 7 valide, remplacez la méthode getImage
par
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();
}
}
Exemple XML
Les 2 xml suivants sont pour Jon Skeet et son homologue earth 2, qui sont tous deux identiques et utilisent donc le même 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"/>
Utiliser l'adaptateur
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());
Liaison d'un espace de noms XML à une classe Java sérialisable.
Ceci est un exemple de fichier package-info.java
qui lie un espace de noms XML à une classe Java sérialisable. Cela doit être placé dans le même paquet que les classes Java qui doivent être sérialisées à l'aide de l'espace de noms.
/**
* 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;
Utiliser XmlAdapter pour rogner la chaîne.
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();
}
}
Et dans package-info.java, ajoutez la déclaration suivante.
@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;