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.

Java SE 7

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&amp;d=identicon&amp;r=PG"/>
<?xml version="1.0" encoding="UTF-8"?>

<user name="Jon Skeet (Earth 2)" image="https://www.gravatar.com/avatar/6d8ebb117e8d83d74ea95fbdd0f87e13?s=328&amp;d=identicon&amp;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;


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow