2016-01-25 75 views
12

Rozważmy następujący kod:Serializować JAXB POJOs wielokrotnych lub różnych nazw

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; 

pragnę ponownie użyć POJO klienta i utworzyć dwie różne wartości nazw. Więc najpierw chciałbym wydrukować wyjście

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

jak aktualny kod ma, następnie utworzyć drugą xml z innej głównej przestrzeni nazw z tej samej POJO który będzie wyglądał tak

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

lub usunąć przestrzeń nazw razem

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

należy umówić kod dodatkowy fragment do * @ XMLSchema * części, ponieważ jest bardzo istotne , ale można go łatwo przeoczyć. – jah

Odpowiedz

7

To jest dobre pytanie.

Zamiast pracować na poziomie typu, można pracować na poziomie węzła Jaxb. Nie ma prawie żadnych kosztów ogólnych.

Klucz tworzy/uzyskuje dostęp do JAXBElement (elementów na poziomie węzła) z klas logiki biznesowej i dostosowuje węzeł XML, który reprezentuje, zgodnie z definicją QName.

QName udostępnia kilka opcji dostosowywania, od w pełni kwalifikowanych przestrzeni nazw do braku przestrzeni nazw. Jedynym zastrzeżeniem jest to, że używanie przestrzeni nazw bez prefiksu nie jest możliwe. Jaxb użyje domyślnej przestrzeni nazw, jeśli jej nie podasz.

Przykład:

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); 

Będzie następujący wynik:

<?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> 

Stosując nazw bez przedrostka nie jest możliwe. Próbuje się QName pustym prefiksu lub równoważną XMLConstants.DEFAULT_NS_PREFIX jako parametr spowoduje domyślnego prefiksu (jak NS1) generowane:

// 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> 

Stosując NULL jako parametr prefix daje wyjątek:

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

pakiet-info.java znajduje się w przykładzie, jest automatycznie generowany przez narzędzia jaxb – danidacar

+1

Jeśli masz wpływ na proces generowania JAXB, możesz spróbować pominąć generowanie package-info.java (przez opcję xnc -npa) – jah

3

Jak zastępują korzeń atrybut xmlns z Moxy:

EclipseLink MOXy zapewnia łatwą personalizację zestawiania JAXB, w tym zmianę domyślnego obszaru nazw elementu głównego. Przedmiot do odwzorowania XML (OXM) nadrzędne domyślnej przestrzeni nazw jest:

<?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> 

nie mieć domyślnej przestrzeni nazw, zamiast używać <xml-schema element-form-default="UNSET"/>.Konfiguracja

Moxy:

1) dodać bibliotekę do projektu:

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

2) Dodaj jaxb.properties do obiektowego modelu pakietu umożliwić fabrykę Moxy'S (npsrc/main/resources/com/sample/entity/jaxb.properties):

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

3) Utwórz instancję tekst w zależności od konfiguracji OXM (w tym przykładzie plik OXM jestsrc/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); 

Następnie można użyć etapowy na JAXBContext jako normalne. Wywołaj osobne konteksty dla każdego innego pliku OXM, którego chcesz użyć.

Dodatkowe modyfikacje z Moxy:

Korzystanie Moxy pozwala na dalsze dostosowywanie rozrządowych swojej jednostki w przyszłości bez zmiany modelu obiektowego, co skutecznie logika marshalling przylegają do open/closed principle chociaż nie masz wyraźne klasy Marshaller. Na przykład, jeśli potrzebne do organizowania swój obiekt Customer jako Person, można to zrobić poprzez dodanie kolejnego pliku 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>