2010-07-20 3 views
24

[bearbeiten Stark wie Verständnis voranschreitet]definiert Frühling JAXB Namespaces ohne NamespacePrefixMapper mit

Ist es möglich, Frühling Jaxb2Marshaller zu verwenden, um einen benutzerdefinierten Satz von Namespacepräfixe (oder zumindest zu respektieren diejenigen in der Schemadatei/Anmerkungen gegeben zu erhalten) ohne eine Erweiterung eines NamespacePrefixMapper verwenden zu müssen?

Die Idee ist, eine Klasse zu haben, mit einem „hat eine“ Beziehung zu einer anderen Klasse, die wiederum eine Eigenschaft mit einem anderen Namespace enthält. Um dies besser zu veranschaulichen, betrachten Sie die folgende Projektskizze, die JDK1.6.0_12 verwendet (das letzte, was ich bei der Arbeit in die Finger bekomme). Ich habe nach dem im Paket org.example.domain:

Main.java:

package org.example.domain; 

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(RootElement.class); 

    RootElement re = new RootElement(); 
    re.childElementWithXlink = new ChildElementWithXlink(); 

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

} 

RootElement.java:

package org.example.domain; 

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

@XmlRootElement(namespace = "www.example.org/abc", name="Root_Element") 
public class RootElement { 
    @XmlElement(namespace = "www.example.org/abc") 
    public ChildElementWithXlink childElementWithXlink; 

} 

ChildElementWithXLink.java:

package org.example.domain; 

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

@XmlRootElement(namespace="www.example.org/abc", name="Child_Element_With_XLink") 
public class ChildElementWithXlink { 
    @XmlAttribute(namespace = "http://www.w3.org/1999/xlink") 
    @XmlSchemaType(namespace = "http://www.w3.org/1999/xlink", name = "anyURI") 
    private String href="http://www.example.org"; 

} 

package-info.java:

@javax.xml.bind.annotation.XmlSchema(
    namespace = "http://www.example.org/abc", 
    xmlns = { 
      @javax.xml.bind.annotation.XmlNs(prefix = "abc", namespaceURI ="http://www.example.org/abc"), 
      @javax.xml.bind.annotation.XmlNs(prefix = "xlink", namespaceURI = "http://www.w3.org/1999/xlink") 
      }, 
    elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) 
    package org.example.domain; 

Lauf Main.main() gibt die folgende Ausgabe:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<ns2:Root_Element xmlns:ns1="http://www.w3.org/1999/xlink" xmlns:ns2="www.example.org/abc"> 
<ns2:childElementWithXlink ns1:href="http://www.example.org"/> 
</ns2:Root_Element> 

während das, was würde Ich mag ist:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<abc:Root_Element xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:abc="www.example.org/abc"> 
<abc:childElementWithXlink xlink:href="http://www.example.org"/> 
</abc:Root_Element> 

Sobald dieser Teil funktioniert, dann ist das Problem der Konfiguration auf die bewegt Jaxb2Marshaller im Frühjahr (Frühjahr 2.5.6, mit Spring-Oxm-Tiger-1.5.6 bietet Jaxb2Marshaller), so dass es das gleiche durch eine einfache Kontextkonfiguration und einen Aufruf von marshal() bereitstellt.

Vielen Dank für Ihr Interesse an diesem Problem!

+0

Können Sie dies mit einem Vanilla 'JAXBContext' arbeiten, d. H. Kein Spring beteiligt? Es sieht so aus, als ob 'Jaxb2Marshaller' für das Problem nicht relevant sein sollte. Welche JDK- und/oder JAXB-Versionen verwenden Sie? (p.s. hallo Gary :) – skaffman

+0

Hey, Skaffman :-) machte die Änderungen nach Bedarf. Das macht meine Nuss. –

Antwort

12

[Einige Änderungen eine JAXB-RI Alternative sind am Ende dieses Beitrags bieten]

Nun, nachdem viel Kopf kratzen ich endlich akzeptieren musste haben, dass für meine Umgebung (JDK1.6.0_12 unter Windows XP und JDK1.6.0_20 auf Mac Leopard) ich kann einfach nicht diese Arbeit machen, ohne die bösen Zuflucht zu suchen, der NamespacePrefixMapper ist. Warum ist es böse? Weil Sie sich in Ihrem Produktionscode auf eine interne JVM-Klasse verlassen müssen. Diese Klassen bilden keinen Teil einer zuverlässigen Schnittstelle zwischen der JVM und Ihrem Code (d. H. Sie wechseln zwischen Aktualisierungen der JVM).

Meiner Meinung zu dieser Antwort hinzufügen könnte Sun sollte dieses Problem oder jemand mit tiefer Kenntnis Adresse - bitte tun!

Weitergehen. Da NamespacePrefixMapper nicht außerhalb der JVM verwendet werden soll ist nicht im Standard-Kompilierung Pfad von javac (einem Teilabschnitt von rt.jar von ct.sym gesteuert) enthalten. Dies bedeutet, dass jeder davon abhängige Code wahrscheinlich in einer IDE kompiliert wird, aber in der Befehlszeile fehlschlägt (d. H. Maven oder Ant). Um dies zu umgehen, muss die Datei rt.jar explizit im Build enthalten sein, und selbst dann scheint Windows Probleme zu haben, wenn der Pfad Leerzeichen enthält.

Wenn Sie sich in dieser Position zu finden, hier ist ein Maven-Schnipsel, die Sie aus der Patsche wird:

<dependency> 
    <groupId>com.sun.xml.bind</groupId> 
    <artifactId>jaxb-impl</artifactId> 
    <version>2.1.9</version> 
    <scope>system</scope> 
    <!-- Windows will not find rt.jar if it is in a path with spaces --> 
    <systemPath>C:/temp/rt.jar</systemPath> 
</dependency> 

Hinweis den Müll hart codierten Pfad zu einem seltsamen Ort für rt.jar. Sie könnten dies mit einer Kombination von {java.home} /lib/rt.jar umgehen, die auf den meisten Betriebssystemen funktioniert, aber aufgrund des Platzproblems von Windows nicht garantiert ist. Ja, Sie Profile verwenden können und entsprechend aktivieren ...

Alternativ in Ant können Sie Folgendes tun:

<path id="jre.classpath"> 
    <pathelement location="${java.home}\lib" /> 
</path> 
// Add paths for build.classpath and define {src},{target} as usual 
<target name="compile" depends="copy-resources"> 
    <mkdir dir="${target}/classes"/> 
    <javac bootclasspathref="jre.classpath" includejavaruntime="yes" debug="on" srcdir="${src}" destdir="${target}/classes" includes="**/*"> 
    <classpath refid="build.classpath"/> 
    </javac> 
</target>  

Und was der Jaxb2Marshaller Spring-Konfiguration? Nun, hier ist es, komplett mit meinem eigenen NamespacePrefixMapper:

Spring:

<!-- JAXB2 marshalling (domain objects annotated with JAXB2 meta data) --> 
<bean id="jaxb2Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller"> 
<property name="contextPaths"> 
    <list> 
    <value>org.example.domain</value> 
    </list> 
</property> 
<property name="marshallerProperties"> 
    <map> 
    <!-- Good for JDK1.6.0_6+, lose 'internal' for earlier releases - see why it's evil? --> 
    <entry key="com.sun.xml.internal.bind.namespacePrefixMapper" value-ref="myCapabilitiesNamespacePrefixMapper"/> 
    <entry key="jaxb.formatted.output"><value type="boolean">true</value></entry> 
    </map> 
</property> 
</bean> 

<!-- Namespace mapping prefix (ns1->abc, ns2->xlink etc) --> 
<bean id="myNamespacePrefixMapper" class="org.example.MyNamespacePrefixMapper"/> 

Dann ist mein NamespacePrefixMapper Code:

public class MyNamespacePrefixMapper extends NamespacePrefixMapper { 

    public String getPreferredPrefix(String namespaceUri, 
           String suggestion, 
           boolean requirePrefix) { 
    if (requirePrefix) { 
     if ("http://www.example.org/abc".equals(namespaceUri)) { 
     return "abc"; 
     } 
     if ("http://www.w3.org/1999/xlink".equals(namespaceUri)) { 
     return "xlink"; 
     } 
     return suggestion; 
    } else { 
     return ""; 
    } 
    } 
} 

Nun gibt es. Ich hoffe, das hilft jemandem, den Schmerz zu vermeiden, den ich durchgemacht habe. Oh, übrigens, können Sie in der folgenden Ausnahme ausgeführt, wenn Sie die oben genannten bösen Ansatz innerhalb Jetty verwenden:

java.lang.IllegalAccessError: Klasse sun.reflect.GeneratedConstructorAccessor23 kann nicht auf seine Super sun.reflect.ConstructorAccessorImpl

Also viel Glück beim Sortieren. Hinweis: rt.jar im bootclasspath Ihres Webservers.

[Zusätzliche Änderungen der JAXB-RI (Referenzimplementierung) Ansatz zeigen]

Wenn Sie in der Lage, die JAXB-RI libraries in Ihren Code einführen können Sie folgende Änderungen vornehmen um den gleichen Effekt zu erhalten:

Main:

// Add a new property that implies external access 
marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new MyNamespacePrefixMapper()); 

MyNamespacePrefixMapper:

// Change the import to this 
import com.sun.xml.bind.marshaller.NamespacePrefixMapper; 

Fügen Sie den folgenden JAR von JAXB-RI-Download (nach Lizenz Reifen springen durch) aus dem Verzeichnis/lib Ordner:

jaxb-impl.jar 

Lauf Main.main() führt zu der gewünschten Ausgabe.

+1

Eine Alternative ist die Verwendung des JAXB-RI anstelle des in JDK 1.6 integrierten. Auf diese Weise erhalten Sie einen vorhersagbaren Klassenpfad, und der 'NamespacePrefixMapper' hat keinen' internen' im Klassennamen :) – skaffman

9

(Stark bearbeitet reponse)

Ich glaube, das Problem in Ihrem Code zu einigen NamespaceURI Mismatches zurückzuführen ist. Manchmal verwenden Sie http://www.example.org/abc und andere Zeiten „www.example.org/abc“. Folgendes sollte den Trick:

Main.java

package org.example.domain; 

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(RootElement.class); 
     System.out.println(jc); 

     RootElement re = new RootElement(); 
     re.childElementWithXlink = new ChildElementWithXlink(); 

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

RootElement.java

package org.example.domain; 

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

@XmlRootElement(namespace="http://www.example.org/abc", name="Root_Element") 
public class RootElement { 
    @XmlElement(namespace = "http://www.example.org/abc") 
    public ChildElementWithXlink childElementWithXlink; 

} 

ChildElementWithXLink.java

package org.example.domain; 

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

@XmlRootElement(namespace="http://www.example.org/abc", name="Child_Element_With_XLink") 
public class ChildElementWithXlink { 
    @XmlAttribute(namespace = "http://www.w3.org/1999/xlink") 
    @XmlSchemaType(namespace = "http://www.w3.org/1999/xlink", name = "anyURI") 
    private String href="http://www.example.org"; 

} 

package-info.java

@javax.xml.bind.annotation.XmlSchema( 
    namespace = "http://www.example.org/abc", 
    xmlns = { 
      @javax.xml.bind.annotation.XmlNs(prefix = "abc", namespaceURI ="http://www.example.org/abc"), 
      @javax.xml.bind.annotation.XmlNs(prefix = "xlink", namespaceURI = "http://www.w3.org/1999/xlink") 
      }, 
    elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) 
    package org.example.domain; 

Jetzt läuft Main.main() gibt die folgende Ausgabe:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<abc:Root_Element xmlns:abc="http://www.example.org/abc" xmlns:xlink="http://www.w3.org/1999/xlink"> 
    <abc:childElementWithXlink xlink:href="http://www.example.org"/> 
</abc:Root_Element> 
+0

Danke Blaise für Ihr Interesse daran. Ich habe einige Änderungen am ursprünglichen Beitrag vorgenommen, um Ihre Fragen besser beantworten zu können. Sie werden feststellen, dass ich ein Child-Element mit einem anderen Namespace (dem XLink) hinzugefügt habe, was ich in meinem ursprünglichen Post nicht erklärt habe (tut mir leid). –

+0

Hallo Blaise, Ich habe versucht, Ihre Ergebnisse mit der aktualisierten RootElement-Klasse zu replizieren, aber leider, wenn alle anderen Code ist wie ich es in den Post geschrieben habe, scheitert es mit einem zusätzlichen (ns3) Namespace. Würde es Ihnen etwas ausmachen, nur zu überprüfen, was ich gepostet habe, um sicherzustellen, dass es immer noch zu Ihrer Einrichtung passt? Vielen Dank für Ihre Hilfe :) –

+0

Ich vermisste die zusätzliche ns3 Namespace-Deklaration. Wenn Sie das @ XmlRootElement von ChildElementWithXLink entfernen, wird es gelöscht (siehe oben bearbeitete Antwort). Benötigen Sie diese Anmerkung in Ihrem Modell? –

0

Die JAXB-Implementierung in JDK 7 unterstützt das Namespacepräfix. Ich habe es mit JDK 1.6.0_21 ohne Glück versucht.