webentwicklung-frage-antwort-db.com.de

Generierung einer JAXB-Klasse, die eine Schnittstelle implementiert

Ich verwende derzeit JAXB, um Java-Klassen zu generieren, um XML zu trennen. Jetzt möchte ich ein neues Schema erstellen, das dem ersten sehr ähnlich ist, und die generierten Klassen dieselbe Schnittstelle implementieren lassen.

Angenommen, ich habe zwei Schemadateien, die XML mit ähnlichen Tags definieren:

adult.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xs:element name="Person">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="Name" type="xs:string" />
      <xs:element name="Age" type="xs:integer" />
      <xs:element name="Job" type="xs:string" />
    </xs:sequence>
  </xs:complexType>
</xs:element>

kid.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xs:element name="Person">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="Name" type="xs:string" />
      <xs:element name="Age" type="xs:integer" />
      <xs:element name="School" type="xs:string" />
    </xs:sequence>
  </xs:complexType>
</xs:element>

Mit JAXB und XJC möchte ich zwei Klassendateien generieren:

public class Adult implements Person {
    ...
    public String getName() { ... }
    public int getAge() { ... }
    public String getJob { ... }
}

public class Kid implements Person {
    ...
    public String getName() { ... }
    public int getAge() { ... }
    public String getSchool { ... }
}

wobei die Personenschnittstelle die Methoden getName() und getAge() definiert.

Ich habe mir einige der documentation für Mapping-Interfaces angesehen, aber dies scheint nur für die Situation zu gelten, wenn Sie bereits Java-Klassen haben, die Sie einem DOM zuordnen möchten.

Ich habe auch versucht, dieses external plugin zu verwenden, aber es scheint nicht zu funktionieren. Hier ist meine XJB-Bindungsdatei:

<jxb:bindings version="1.0" 
  xmlns:jxb="http://Java.Sun.com/xml/ns/jaxb" 
  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
  xmlns:xjc="http://Java.Sun.com/xml/ns/jaxb/xjc"
  xmlns:ext="http://xml.w-wins.com/xjc-plugins/interfaces"
  jxb:extensionBindingPrefixes="xjc">

    <jxb:bindings schemaLocation="xsd/adult.xsd" node="xs:schema/xs:complexType[@name='Person']">
        <ext:interface>mypackage.Hello</ext:interface> 
    </jxb:bindings>

</jxb:bindings>

dies führt jedoch zu folgendem Fehler:

$ Java -cp "lib/activation.jar;lib/InterfacesXJCPlugin.jar;lib/jaxb1-impl.jar;lib/jaxb-api.jar;lib/jaxb-xjc.jar;lib/jsr173_1.0_api.jar" com.Sun.tools.xjc.XJCFacade -p mypackage.myxml -extension -Xinterfaces xsd/adult.xsd -b binding.xjb
parsing a schema...
[ERROR] XPath evaluation of "xs:schema/xs:complexType[@name='Person']" results in empty target node
  line 8 of file:/C:/dev/jaxb/jaxb-ri/binding.xjb

Failed to parse a schema.

Kann mit JAXB eine Klasse generiert werden, die eine Schnittstelle implementiert?

Update

Ich habe versucht, das Interface Insertion plugin zu verwenden, aber aus irgendeinem Grund kann es nicht funktionieren. So rufe ich xjc an, aber es ist, als würde das Plugin-Jar nicht vom Klassenpfad abgerufen:

$ Java -cp "lib/xjc-if-ins.jar;lib/jaxb-xjc.jar" com.Sun.tools.xjc.XJCFacade -p mypackage -extension -Xifins myschema.xsd -b binding.xjb

Ich bekomme den Fehler:

unrecognized parameter -Xifins

Irgendwelche Ideen?

48
Alex Spurling

Leider sieht es so aus, als würde das Plugin für Schnittstelleninjektionen, das in einigen anderen Antworten erwähnt wurde, nicht mehr gut unterstützt. Tatsächlich habe ich Probleme, das JAR zum Download zu finden.

Glücklicherweise bieten die JAXB2 Basics Plugins einen ähnlichen Mechanismus zum Hinzufügen einer Schnittstelle zu den generierten JAXB-Stubs (siehe das Inheritance Plugin ). 

Die Dokumentation des Inheritance-Plugins enthält ein Beispiel, das zeigt, wie die XML-Schemadatei aussehen könnte. Da Sie das Schema jedoch nicht ändern können, können Sie stattdessen eine externe Bindungsdatei verwenden:

<?xml version="1.0"?>
<jxb:bindings version="1.0" 
  xmlns:jxb="http://Java.Sun.com/xml/ns/jaxb" 
  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
  xmlns:xjc="http://Java.Sun.com/xml/ns/jaxb/xjc"
  xmlns:inheritance="http://jaxb2-commons.dev.Java.net/basic/inheritance"
  jxb:extensionBindingPrefixes="xjc">

    <jxb:bindings schemaLocation="xsd/adult.xsd">
      <jxb:bindings node="//xs:complexType[@name='Person']">
        <inheritance:implements>mypackage.Hello</inheritance:implements> 
      </jxb:bindings>
    </jxb:bindings>

</jxb:bindings>

Die Dokumentation zu den JAXB2-Grundlagen-Plugins enthält Anweisungen zur Verwendung des Plugins mit Ant und Maven. Sie können es auch direkt von der Befehlszeile aus verwenden, der Befehl ist jedoch etwas unordentlich (aufgrund der Anzahl der Gläser, die Sie dem Klassenpfad hinzufügen müssen):

Java -jar jaxb-xjc.jar 
     -classpath jaxb2-basics-0.5.3.jar,jaxb2-basics-runtime-0.5.3.jar,
                jaxb2-basics-tools-0.5.3.jar,commons-beanutils-0.5.3.jar,
                commons-lang.jar,commons-logging.jar
     -p mypackage.myxml -extension -Xinheritance xsd/adult.xsd -b binding.xjb

Die JAXB2 Basics Plugins enthalten eine Reihe weiterer Dienstprogramme, die möglicherweise ebenfalls nützlich sind (z. B. die automatische Generierung von Gleichgestellten, Hash-Code und toString-Methoden).

43
Jim Hurne

Es könnte für Ihre Situation übertrieben sein, aber ich habe dies mit AspectJ erledigt (wir haben bereits Aspekte dieses Projekts verwendet, also hatten wir bereits die Abhängigkeit und das Risiko).

Sie würden einen Aspekt wie folgt erklären:

public aspect MyAspect
{
    declare parents: 
        com.foo.generated.Adult
    implements com.foo.Person;

    declare parents: 
        com.foo.generated.Kid
    implements com.foo.Person;
}

Dadurch wird die Schnittstelle com.foo.Person zu den Klassen com.foo.generated.Adult und com.foo.generated.Adult hinzugefügt.

Vielleicht übertrieben für Ihren Zweck, aber es hat für uns funktioniert.

8
Chris

Die Antwort, die für mich funktionierte, war Jim Hurnes Beispiel für die Verwendung des JAXB2-Basics-Plugins. Aber die Dokumentation, die er verlinkt hat, scheint nicht mehr verfügbar zu sein, also habe ich das Maven-Plugin so konfiguriert:

        <plugin>
            <groupId>org.jvnet.jaxb2.maven2</groupId>
            <artifactId>maven-jaxb2-plugin</artifactId>
            <version>0.8.2</version>
            <executions>
                <execution>
                    <goals>
                        <goal>generate</goal>
                    </goals>
                    </execution>
            </executions>
            <configuration>
                <extension>true</extension>
                <args>
                    <arg>-Xinheritance</arg>
                </args>
                <bindingDirectory>src/main/resources/xjb</bindingDirectory>
                <bindingIncludes>
                    <include>**.xml</include> <!-- This Should reference the binding files you use to configure the inheritance -->
                </bindingIncludes>
                <schemaDirectory>src/main/resources/xsd</schemaDirectory>
                <generateDirectory>${project.build.directory}/generated-sources/jaxb</generateDirectory>
                <generatePackage>mypackage</generatePackage>
                <plugins>
                    <plugin>
                        <groupId>org.jvnet.jaxb2_commons</groupId>
                        <artifactId>jaxb2-basics</artifactId>
                        <version>0.5.3</version>
                    </plugin>
                </plugins>
            </configuration>
        </plugin>
3
Alex Spurling

Dies kann in diesem einfachen Fall ohne Verwendung eines Drittanbieter-Plugins mit der JAXB RI Vendor Extensions xjc: superInterface-Anpassung erreicht werden. Es ist zu beachten, dass dieser Ansatz dazu führt, dass alle generierten Klassen die Schnittstelle implementieren. 

Beispiel für eine Bindungsdatei:

<jxb:bindings version="1.0" 
  xmlns:jxb="http://Java.Sun.com/xml/ns/jaxb" 
  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
  xmlns:xjc="http://Java.Sun.com/xml/ns/jaxb/xjc" 
  jxb:extensionBindingPrefixes="xjc">
    <jxb:bindings schemaLocation="adult.xsd" node="/xs:schema">
    <jxb:globalBindings>
        <xjc:superInterface name="com.example.model.Person"/>
    </jxb:globalBindings>
    </jxb:bindings>
</jxb:bindings>

Sie müssen dann nur xjc ausführen, die Bindungsdatei angeben und das Erweiterungsflag setzen. Viel schneller/einfacher als JAXB2Basics einzuführen!

Ich war anfangs skeptisch, dass dies funktionieren würde, wie in der Dokumentation heißt:

Durch die Anpassung können Sie den vollständig qualifizierten Namen der Java-Schnittstelle angeben, die als Root-Schnittstelle aller generierten Schnittstellen verwendet werden soll. Diese Anpassung wirkt sich nicht aus, es sei denn, Sie generieren Schnittstellen mit dem Schalter globalBindings generateValueClass = "false".

Als ich es jedoch mit Bindungen ausprobierte, die der obigen ähnelten (ohne Ersetzen von generateValueClass = "false" und Generieren von Klassen, nicht von Interfaces), erhielt ich die Ausgabe, die ich brauchte - alle meine generierten Klassen implementierten das Common Interface. 

3
Malcolm Smith

In meinem Fall funktioniert der Befehlszeilenaufruf über Java -jar:

Java -jar $somepath/jaxb-xjc.jar -classpath $somepath/xjc-if-ins.jar my.xsd -d $destdir -b $bindingconfig -p $desiredpackage -extension -Xifins

Bei der Ausführung der xjc ant-Task bleibt der Fehler jedoch bestehen. Die angezeigte Fehlermeldung ist irritierend, da der wahre Grund in meinem Fall eine fehlerhafte Versionsnummer in einer Klassendatei ist, die ant lädt (siehe stacktrace unten). Diese korrekte Fehlermeldung wird nur angezeigt, wenn Sie ANT_OPTS Folgendes hinzufügen: -Dcom.Sun.tools.xjc.Options.findServices=true

[xjc] Java.lang.UnsupportedClassVersionError: Bad version number in .class file
[xjc]     at Java.lang.ClassLoader.defineClass1(Native Method)
[xjc]     at Java.lang.ClassLoader.defineClass(ClassLoader.Java:620)
[xjc]     at org.Apache.tools.ant.AntClassLoader.defineClassFromData(AntClassLoader.Java:1134)
[xjc]     at org.Apache.tools.ant.AntClassLoader.getClassFromStream(AntClassLoader.Java:1320)
[xjc]     at org.Apache.tools.ant.AntClassLoader.findClassInComponents(AntClassLoader.Java:1376)
[xjc]     at org.Apache.tools.ant.AntClassLoader.findClass(AntClassLoader.Java:1336)
[xjc]     at org.Apache.tools.ant.AntClassLoader.loadClass(AntClassLoader.Java:1074)
[xjc]     at Java.lang.ClassLoader.loadClass(ClassLoader.Java:299)
[xjc]     at Java.lang.ClassLoader.loadClass(ClassLoader.Java:251)
[xjc]     at com.Sun.tools.xjc.Options.findServices(Options.Java:936)
[xjc]     at com.Sun.tools.xjc.Options.getAllPlugins(Options.Java:336)
[xjc]     at com.Sun.tools.xjc.Options.parseArgument(Options.Java:632)
[xjc]     at com.Sun.tools.xjc.Options.parseArguments(Options.Java:742)
[xjc]     at com.Sun.tools.xjc.XJC2Task._doXJC(XJC2Task.Java:444)
[xjc]     at com.Sun.tools.xjc.XJC2Task.doXJC(XJC2Task.Java:434)
[xjc]     at com.Sun.tools.xjc.XJC2Task.execute(XJC2Task.Java:369)
[xjc]     at com.Sun.istack.tools.ProtectedTask.execute(ProtectedTask.Java:55)
[xjc]     at org.Apache.tools.ant.UnknownElement.execute(UnknownElement.Java:291)
[xjc]     at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[xjc]     at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:39)
[xjc]     at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:25)
[xjc]     at Java.lang.reflect.Method.invoke(Method.Java:585)
[xjc]     at org.Apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.Java:106)
[xjc]     at org.Apache.tools.ant.Task.perform(Task.Java:348)
[xjc]     at org.Apache.tools.ant.Target.execute(Target.Java:390)
[xjc]     at org.Apache.tools.ant.Target.performTasks(Target.Java:411)
[xjc]     at org.Apache.tools.ant.Project.executeSortedTargets(Project.Java:1360)
[xjc]     at org.Apache.tools.ant.Project.executeTarget(Project.Java:1329)
[xjc]     at org.Apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.Java:41)
[xjc]     at org.Apache.tools.ant.Project.executeTargets(Project.Java:1212)
[xjc]     at org.Apache.tools.ant.Main.runBuild(Main.Java:801)
[xjc]     at org.Apache.tools.ant.Main.startAnt(Main.Java:218)
[xjc]     at org.Apache.tools.ant.launch.Launcher.run(Launcher.Java:280)
[xjc]     at org.Apache.tools.ant.launch.Launcher.main(Launcher.Java:109)
[xjc]
[xjc] failure in the XJC task. Use the Ant -verbose switch for more details
1
cfsh

In der Dokumentation für das Plug-In zum Einfügen von Schnittstellen wird Folgendes vorgeschlagen

[ Um xjc mit dem Interface Insertion Plugin von der Befehlszeile aus aufzurufen, können Sie schreiben:

Java -cp $JAXB_HOME/share/lib/xjc-if-ins.jar -extension -Xifins schema

]

Ich vermute, dass Sie die Hauptmethode der falschen Klasse aufrufen - com.Sun.tools.xjc.XJCFacade. Sie sollten wahrscheinlich die genaue Syntax erneut versuchen. 

Hier ist ein Link in einem anderen Forum, in dem ein ähnliches Problem behandelt wird . http://forums.Java.net/jive/message.jspa?messageID=220686

  • Ich hätte dies als Kommentar gepostet, aber ich habe nicht genug Punkte zum Kommentieren.
0
Thimmayya

Was ist, wenn Sie den Typ in eine gemeinsame XSD-Datei extrahieren, die in adult.xsd und kid.xsd importiert wird? Oder haben Sie eine vorhandene Schnittstelle, die Sie implementieren müssen?

0
Mirvnillith

Für diejenigen, die beim Erstellen von JAXB-Datenbindungsdateien für komplexere Schemas helfen möchten - das neueste Open-Source-XML-Tool - CAM Editor v2.2 unterstützt dies jetzt.

Sie können auf die Ressourcen und die Download-Site zugreifen, um eine Schritt-für-Schritt-Anleitung zu erhalten und den Download-Abschnitt für das Tool oben zu sehen.

http://www.cameditor.org/#JAXB_Bindings

Genießen.

0
user841942