Генерация класса JAXB, который реализует интерфейс
В настоящее время я использую JAXB для генерации Java-классов, чтобы разобрать XML. Теперь я хотел бы создать новую схему, очень похожую на первую, и иметь сгенерированные классы, реализующие один и тот же интерфейс.
Например, у меня есть два файла схемы, которые определяют XML с похожими тегами:
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>
Используя JAXB и XJC, я хотел бы создать два файла классов:
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 { ... }
}
где интерфейс Person определяет getName()
а также getAge()
методы.
Я посмотрел на некоторую документацию по отображению интерфейсов, но, похоже, это относится только к ситуации, когда у вас уже есть Java-классы, которые вы хотите отобразить в DOM.
Кроме того, я пытался использовать этот внешний плагин, но он не работает. Вот мой файл связывания xjb:
<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>
но это дает следующую ошибку:
$ 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.
Можно ли создать класс с JAXB, который реализует интерфейс?
Обновить
Я пытался использовать плагин Interface Insertion, но по какой-то причине не могу заставить его работать. Вот как я называю xjc, но это похоже на то, что jar плагина не берется из classpath:
$ 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
Я получаю ошибку:
unrecognized parameter -Xifins
Есть идеи?
9 ответов
К сожалению, похоже, что плагин внедрения интерфейса, упомянутый в некоторых других ответах, больше не поддерживается. На самом деле, у меня проблемы с поиском JAR для загрузки.
К счастью, базовые плагины JAXB2 предоставляют аналогичный механизм для добавления интерфейса к сгенерированным заглушкам JAXB (см. Плагин Inheritance).
В документации к плагину Inheritance есть пример, показывающий, как может выглядеть файл схемы XML. Однако, поскольку вы не можете изменить схему, вы можете использовать вместо этого внешний файл привязок:
<?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>
Документация по плагинам JAXB2 Basics содержит инструкции по использованию плагина с Ant и Maven. Вы также можете использовать его прямо из командной строки, но команда немного запутана (из-за количества банок, которые вы должны добавить в путь к классам):
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
Подключаемые модули JAXB2 Basics предоставляют ряд других утилит, которые также могут оказаться полезными (например, автоматическое создание методов equals, hashCode и toString).
Это может быть излишним для вашей ситуации, но я сделал это с помощью AspectJ (мы уже использовали аспекты в этом проекте, поэтому у нас уже была зависимость и воздействие).
Вы бы объявили аспект в соответствии с:
public aspect MyAspect
{
declare parents:
com.foo.generated.Adult
implements com.foo.Person;
declare parents:
com.foo.generated.Kid
implements com.foo.Person;
}
Который добавит интерфейс com.foo.Person
на занятия com.foo.generated.Adult
а также com.foo.generated.Adult
Может быть, излишним для вашей цели, но это сработало для нас.
Ответ, который работал для меня, был примером Джима Херна об использовании плагина JAXB2 Basics. Но документация, которую он связал, кажется, больше недоступна, поэтому для справки я настроил плагин maven:
<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>
Это можно сделать для этого простого случая без использования стороннего плагина, используя настройки JAXB RI Vendor Extensions xjc: superInterface. Следует отметить, что этот подход заставит все сгенерированные классы реализовать интерфейс.
Пример файла привязок:
<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>
Затем вам просто нужно запустить xjc, указав файл привязки и установив флаг -extension. Гораздо быстрее / проще, чем вводить JAXB2Basics!
Сначала я скептически относился к тому, что это будет работать, как говорится в документации:
Настройка позволяет вам указать полностью определенное имя интерфейса Java, которое должно использоваться в качестве корневого интерфейса всех сгенерированных интерфейсов. Эта настройка не имеет никакого эффекта, если вы специально не генерируете интерфейсы с переключателем globalBindings generateValueClass="false".
Но когда я опробовал его с привязками, аналогичными приведенным выше (без указания generateValueClass = "false" и создания классов, а не интерфейсов), он дал мне требуемый вывод - все мои сгенерированные классы реализовали общий интерфейс.
В моем случае вызов командной строки через java -jar работает:
java -jar $somepath/jaxb-xjc.jar -classpath $somepath/xjc-if-ins.jar my.xsd -d $destdir -b $bindingconfig -p $desiredpackage -extension -Xifins
Однако при выполнении муравьиной задачи xjc ошибка остается. Данное сообщение об ошибке раздражает, так как реальная причина в моем случае - неверный номер версии в файле класса, который пытается загрузить муравей (см. Трассировку стека ниже). Это правильное сообщение об ошибке появляется, только когда вы добавляете в ANT_OPTS следующее:-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
Для тех, кто хотел бы помочь в создании файлов привязки данных JAXB для более сложной схемы - последний инструмент с открытым исходным кодом XML - CAM Editor v2.2 теперь поддерживает это.
Вы можете получить доступ к ресурсам и сайту загрузки для пошагового руководства и просмотреть раздел загрузок в верхней части страницы для инструмента.
Наслаждаться.
Что если вы извлечете тип в общий XSD, импортированный в adult.xsd и kid.xsd? Или у вас есть существующий интерфейс, который вам нужен для реализации?
Документация для плагина для вставки интерфейса предлагает следующее
[Чтобы вызвать xjc с помощью плагина вставки интерфейса из командной строки, вы можете написать:
java -cp $JAXB_HOME/share/lib/xjc-if-ins.jar -extension -Xifins schema
]
Я предполагаю, что вы вызываете основной метод неправильного класса - com.sun.tools.xjc.XJCFacade. Возможно, вам следует повторить попытку с точным синтаксисом.
Вот ссылка на другом форуме, где обсуждается аналогичная проблема. http://forums.java.net/jive/message.jspa?messageID=220686
- Я бы опубликовал это как комментарий, но у меня недостаточно очков, чтобы комментировать.
XJC-плагин некоторого описания является ответом на вашу проблему, это просто вопрос поиска того, который работает. Лучший источник для них здесь:
https://jaxb2-commons.dev.java.net/
В частности, этот: