Generieren einer JAXB-Klasse, die eine Schnittstelle implementiert
Lesezeit: 11 Minuten
Alex Spurling
Ich verwende derzeit JAXB, um Java-Klassen zu generieren, um XML zu entmarshallieren. 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:
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 Person-Schnittstelle die definiert getName() und getAge() Methoden.
Ich habe mir einige angeschaut Dokumentation für die Zuordnung von Schnittstellen, aber dies scheint nur für die Situation zu gelten, in der Sie bereits Java-Klassen haben, die Sie einem DOM zuordnen möchten.
Außerdem habe ich versucht, dies zu verwenden externes Plugin aber es scheint nicht zu funktionieren. Hier ist meine XJB-Bindungsdatei:
$ 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.
Ist es möglich, mit JAXB eine Klasse zu generieren, die eine Schnittstelle implementiert?
Aktualisieren
Ich habe versucht, die zu verwenden Schnittstelleneinfügung plugin, kann es aber aus irgendeinem Grund nicht zum Laufen bringen. So rufe ich xjc auf, aber es ist, als ob das Plugin-Jar nicht vom Klassenpfad abgeholt wird:
Das Interface Insertion Plugin kann verwendet werden, um eine aus einem XSD-Element generierte Klasse dazu zu bringen, eine bestimmte Schnittstelle zu implementieren. Hier möchten Sie eine Klasse basierend auf ihrem Schemanamen generieren – dazu benötigen Sie ein anderes Plugin. Ich habe das Einfügen der Schnittstelle erfolgreich mit maven zum Laufen gebracht. Lassen Sie mich wissen, wenn Sie die Details benötigen. Die Quellen der Schnittstelleneinfügung sind hier: jaxb2-commons.dev.java.net/interface-insertion/…
– rochb
13. November 2009 um 16:43 Uhr
ist der richtige Weg.
– rochb
21. November 2009 um 14:44 Uhr
Jim Hurne
Leider sieht es so aus, als ob das in einigen anderen Antworten erwähnte Interface-Injection-Plugin nicht mehr gut unterstützt wird. Tatsächlich habe ich Probleme, das JAR zum Herunterladen zu finden.
Zum Glück ist die JAXB2-Grundlagen-Plugins bietet einen ähnlichen Mechanismus zum Hinzufügen einer Schnittstelle zu den generierten JAXB-Stubs (siehe die Vererbungs-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:
Die JAXB2 Basics Plugins-Dokumentation enthält Anweisungen zur Verwendung des Plugins mit Ant und Maven. Sie können es auch direkt von der Befehlszeile aus verwenden, aber der Befehl ist etwas chaotisch (aufgrund der Anzahl der JAR-Dateien, die Sie dem Klassenpfad hinzufügen müssen):
Die JAXB2 Basics Plugins bieten eine Reihe anderer Dienstprogramme, die Sie möglicherweise ebenfalls nützlich finden (z. B. die automatische Generierung von Gleichheits-, HashCode- und toString-Methoden).
Ihr Beispiel nutzt eine Erweiterung oder Core-Fähigkeit der maven-jaxb2-plugin … gibt es eine ähnliche Erweiterung für cxf-xjc-plugin damit können wir so etwas wie einrichten -Xinheritance?
Außerdem sollte der richtige XPATH für das gegebene Beispiel //xs:element sein[@name=’Person’]/xs:komplexerTyp[1]
– Roy B
30. Dezember 2014 um 13:57 Uhr
jaxb-xjc.jar von maven gibt no main in METAINF Error. Woher bekommen wir jaxb-xjc.jar, das mit ausgeführt wird? java -jar Möglichkeit ?
– ulab
17. Mai 2016 um 11:34 Uhr
Ich habe gerade 2 Stunden beim Debuggen dieser Lösung verloren … es ist sehr wichtig, Knoten wie diesen zu deklarieren node=”/xs:schema/xs:element[@name=’Person’]/xs:complexType”. Im Gegensatz dazu wird das Vererbungs-Tag ignoriert
– Levijatanu
25. Februar 2020 um 9:24 Uhr
Chris
Es könnte für Ihre Situation übertrieben sein, aber ich habe dies mit AspectJ gemacht (wir haben bereits Aspekte in diesem Projekt verwendet, also hatten wir bereits die Abhängigkeit und die Exposition).
Welches wird die Schnittstelle hinzufügen com.foo.Person zu den Klassen com.foo.generated.Adult und com.foo.generated.Kid
Könnte für Ihren Zweck übertrieben sein, aber es hat für uns funktioniert.
Ich denke, das funktioniert nur mit Weben zur Kompilierzeit, sonst beschweren sich Marshaller über das Extra ajc$instance Eigenschaften.
– Orangenhund
22. September 2016 um 16:36 Uhr
Die Antwort, die für mich funktioniert hat, war Jim Hurnes Beispiel für die Verwendung des JAXB2 Basics-Plugins. Die von ihm verlinkte Dokumentation scheint jedoch nicht mehr verfügbar zu sein. Als Referenz 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>
das hat perfekt funktioniert. Die Versionen sind jedoch ziemlich alt und ich konnte nicht die gleiche Lösung mit den “neuesten” Versionen zum Laufen bringen
– Sean
28. Juni 2016 um 16:15 Uhr
Für diesen einfachen Fall ist es möglich, dies zu erreichen, ohne ein Plug-in eines Drittanbieters zu verwenden JAXB RI Anbietererweiterungen xjc:superInterface-Anpassung. Es sollte beachtet werden, dass dieser Ansatz bewirkt, dass alle generierten Klassen die Schnittstelle implementieren.
Sie müssen dann nur noch laufen xjc Angabe der Bindungsdatei und Setzen des Flags -extension. Viel schneller/einfacher als das Einbringen von JAXB2Basics!
Ich war anfangs skeptisch, ob dies funktionieren würde, da in der Dokumentation angegeben ist:
Die Anpassung ermöglicht es Ihnen, den vollständig qualifizierten Namen der Java-Schnittstelle anzugeben, die als Root-Schnittstelle aller generierten Schnittstellen verwendet werden soll. Diese Anpassung hat keine Auswirkungen, es sei denn, Sie generieren speziell Schnittstellen mit dem Schalter globalBindings generateValueClass=”false”.
Aber als ich es mit einer ähnlichen Bindung wie der obigen ausprobierte (ohne GenerateValueClass=”false” anzugeben und Klassen, nicht Schnittstellen zu generieren), gab es mir die gewünschte Ausgabe – alle meine generierten Klassen implementierten die gemeinsame Schnittstelle.
cfsch
In meinem Fall funktioniert der Kommandozeilenaufruf über java -jar:
Beim Ausführen der xjc-Ant-Task bleibt der Fehler jedoch bestehen. Die angezeigte Fehlermeldung ist irritierend, da der wahre Grund in meinem Fall eine falsche Versionsnummer in einer Klassendatei ist, die Ameise zu laden versucht (siehe Stacktrace unten). Diese korrekte Fehlermeldung erscheint nur, wenn Sie Folgendes zu ANT_OPTS 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
Thimmayya
Die Dokumentation für das Interface-Insertion-Plugin schlägt Folgendes vor
[
To call xjc with the Interface Insertion Plugin from the command line, you may write:
Ich vermute, dass Sie die Hauptmethode der falschen Klasse aufrufen – com.sun.tools.xjc.XJCFacade. Sie sollten es wahrscheinlich mit der genauen Syntax erneut versuchen.
Ich hätte dies als Kommentar gepostet, aber ich habe nicht genug Punkte, um einen Kommentar abzugeben.
Skaffmann
Ein XJC-Plygin irgendeiner Beschreibung ist die Antwort auf Ihr Problem, es geht nur darum, einen zu finden, der funktioniert. Die beste Quelle für sie ist hier:
Das Interface Insertion Plugin kann verwendet werden, um eine aus einem XSD-Element generierte Klasse dazu zu bringen, eine bestimmte Schnittstelle zu implementieren. Hier möchten Sie eine Klasse basierend auf ihrem Schemanamen generieren – dazu benötigen Sie ein anderes Plugin. Ich habe das Einfügen der Schnittstelle erfolgreich mit maven zum Laufen gebracht. Lassen Sie mich wissen, wenn Sie die Details benötigen. Die Quellen der Schnittstelleneinfügung sind hier: jaxb2-commons.dev.java.net/interface-insertion/…
– rochb
13. November 2009 um 16:43 Uhr
– rochb
21. November 2009 um 14:44 Uhr