2016-01-25 50 views
12

Si consideri il seguente codice:Serialize JAXB Pojos a multipli o spazi dei nomi diversi

Main.java 
==== 
package com.sample; 

import com.sample.entity.Customer; 
import javax.xml.bind.JAXBContext; 
import javax.xml.bind.JAXBException; 
import javax.xml.bind.Marshaller; 

public class Main {  

    public static void main(String[] args) throws JAXBException { 
     JAXBContext jc = JAXBContext.newInstance(Customer.class); 

     Customer customer = new Customer(); 
     customer.setId(123); 

     Marshaller m = jc.createMarshaller(); 
     m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 
     m.marshal(customer, System.out);  
    } 
} 


Customer.java 
==== 
package com.sample.entity; 

import javax.xml.bind.annotation.XmlAttribute; 
import javax.xml.bind.annotation.XmlRootElement; 

@XmlRootElement 
public class Customer { 
    private long id; 

    public long getId() { 
     return id; 
    } 

    public void setId(long id) { 
     this.id = id; 
    } 
} 

package-info.java 
==== 
@XmlSchema(namespace = "http://www.example.org/package", elementFormDefault = XmlNsForm.QUALIFIED) 
package com.sample.entity; 

import javax.xml.bind.annotation.XmlSchema; 
import javax.xml.bind.annotation.XmlNsForm; 

desidero riutilizzare il POJO Cliente e creare due differenti valori dello spazio dei nomi. Quindi, prima desidero stampare l'output

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<customer xmlns="http://www.example.org/package"> 
    <id>123</id> 
</customer> 

come il codice corrente non, quindi creare un secondo xml con un diverso namespace principale dalla stessa POJO che sarà simile a questa

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<customer xmlns="http://www.another.org/package"> 
    <id>123</id> 
</customer> 

o rimuovere la namespace tutti insieme

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<customer> 
    <id>123</id> 
</customer> 
+0

si dovrebbe fare un frammento di codice in più per il * @ XmlSchema * parte in quanto è molto rilevante , ma può essere facilmente trascurato. – jah

risposta

7

Questa è una buona domanda.

Invece di lavorare a livello di tipo, è possibile lavorare sul livello del nodo Jaxb. Non c'è quasi nessun sovraccarico.

Il tasto crea/accede a JAXBElement (elementi a livello di nodo) fuori dalle classi di logica aziendale e personalizza il nodo XML che rappresenta con una definizione QName corrispondente.

QName fornisce diverse opzioni per la personalizzazione, che vanno da spazi dei nomi completi a nessuno spazio dei nomi. L'unica avvertenza è che l'uso di uno spazio dei nomi senza prefisso non è possibile. Jaxb userà uno spazio dei nomi predefinito se non ne fornisci uno.

Esempio:

QName elaborateQName = new QName("http://www.another.org/package", "customer", "myNs"); 
QName simpleQName = new QName("customer"); 

JAXBElement jx1 = new JAXBElement(elaborateQName, customer.getClass(), customer); 
JAXBElement jx2 = new JAXBElement(simpleQName, customer.getClass(), customer); 

m.marshal(jx1, System.out); 
m.marshal(jx2, System.out); 

Ciò produrrà il seguente output:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<myNs:customer xmlns:myNs="http://www.another.org/package"> 
    <id>123</id> 
</myNs:customer> 

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<customer> 
    <id>123</id> 
</customer> 

Utilizzando un namespace senza prefisso non è possibile. Cercando un QName con vuoto prefisso o l'equivalente XMLConstants.DEFAULT_NS_PREFIX come parametro si tradurrà in un prefisso predefinito (come NS1) essendo generato:

// QName defaultNs = new QName("http://www.another.org/package", "customer", ""); 

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<ns2:customer xmlns:ns1="http://www.another.org/package"> 
    <id>123</id> 
</ns2:customer> 

Uso nullo come parametro per prefix cede un'eccezione:

java.lang.IllegalArgumentException: prefix cannot be "null" when creating a QName 
at javax.xml.namespace.QName.<init>(QName.java:251) 
+0

package-info.java si trova nell'esempio, è generato da strumenti jaxb – danidacar

+1

Se si ha influenza sul processo di generazione JAXB, si può provare a saltare la generazione di package-info.java (tramite l'opzione -npa di xjc) – jah

3

Come ignorare la radice attributo xmlns con Moxy:

EclipseLink MOXy fornisce una facile personalizzazione del marshalling JAXB, inclusa la modifica dello spazio dei nomi predefinito dell'elemento root. L'oggetto di mapping XML (OXM) per superiori lo spazio dei nomi di default è:

<?xml version="1.0"?> 
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm" version="2.5"> 
    <xml-schema element-form-default="QUALIFIED" namespace="http://www.another.org/package"/> 
</xml-bindings> 

non avere spazio dei nomi predefinito, utilizzare <xml-schema element-form-default="UNSET"/> invece.Setup

Moxy:

1) aggiungere la libreria al progetto:

<dependency> 
    <groupId>org.eclipse.persistence</groupId> 
    <artifactId>org.eclipse.persistence.moxy</artifactId> 
    <version>2.6.2</version> 
</dependency> 

2) Aggiungere un jaxb.properties al vostro pacchetto modello a oggetti per consentire fabbrica di Moxy (esempiosrc/main/resources/com/sample/entity/jaxb.properties):

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory 

3) Istituire il vostro cono testo in base alla configurazione OXM (in questo esempio, il file di OXM è asrc/main/resources/com/sample/entity/my-oxm.xml):

Map<String, Source> metadata = Collections.singletonMap("com.sample.entity", new StreamSource(Customer.class.getResourceAsStream("my-oxm.xml"))); 
Map<String, Object> properties = Collections.singletonMap(JAXBContextProperties.OXM_METADATA_SOURCE, metadata); 
JAXBContext jaxbContext = JAXBContext.newInstance(new Class[] {customer.getClass()}, properties); 

È quindi possibile utilizzare smistamento sul JAXBContext come normale. Istanziare contesti separati per ogni diverso file OXM che si desidera utilizzare.

personalizzazioni aggiuntive con moxy:

Utilizzando Moxy consente di personalizzare ulteriormente lo smistamento della vostra entità in futuro senza cambiare il modello a oggetti, di fatto rendendo la logica di smistamento aderire alla open/closed principle anche se non si ha classi esplicite Marshaller. Per esempio, se è necessario il marshalling l'oggetto Customer come Person, si potrebbe fare questo con l'aggiunta di un altro file OXM:

<?xml version="1.0"?> 
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm" version="2.5"> 
    <xml-schema element-form-default="QUALIFIED" namespace="http://www.example.com/person"/> 
    <java-types> 
     <java-type name="com.sample.entity.Customer"> 
      <xml-root-element name="person"/> 
      <java-attributes> 
       <xml-attribute java-attribute="id" name="personId"/> 
       <xml-element java-attribute="id" xml-path="someOtherId/text()"/> 
      </java-attributes> 
     </java-type> 
    </java-types> 
</xml-bindings>