Файл привязки JAXB: XmlAdapters и имя пакета
У меня есть такой файл привязки
<jxb:bindings version="2.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<jxb:bindings schemaLocation="example.xsd" node="/xs:schema">
<jxb:schemaBindings>
<jxb:package name="example" />
</jxb:schemaBindings>
<jxb:globalBindings>
<jxb:javaType name="java.util.Calendar" xmlType="xs:dateTime"
parseMethod="javax.xml.bind.DatatypeConverter.parseDateTime"
printMethod="javax.xml.bind.DatatypeConverter.printDateTime" />
<jxb:javaType name="java.util.Calendar" xmlType="xs:date"
parseMethod="javax.xml.bind.DatatypeConverter.parseDate"
printMethod="javax.xml.bind.DatatypeConverter.printDate" />
<jxb:javaType name="java.util.Calendar" xmlType="xs:time"
parseMethod="javax.xml.bind.DatatypeConverter.parseTime"
printMethod="javax.xml.bind.DatatypeConverter.printTime" />
</jxb:globalBindings>
</jxb:bindings>
</jxb:bindings>
Класс схемы генерируется в "примере" (правильно), но XmlAdapters в "org.w3._2001.xmlschema" (неправильно). Как я могу это исправить?
5 ответов
Для пользователей Apache CXF самым чистым способом является использование -p
вариант, предлагаемый wsdl2java
,
-p [wsdl-namespace=]PackageName
Задает ноль или более имен пакетов для использования в сгенерированном коде. Опционально указывает пространство имен WSDL для сопоставления имен пакетов.
В нашем случае
-p http://www.w3.org/2001/XMLSchema=org.acme.foo
Если вы используете плагин cxf-codegen-plugin, просто добавьте еще одну пару <extraarg>
,
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>${cxf.version}</version>
[...]
<extraarg>-p</extraarg>
<extraarg>http://www.w3.org/2001/XMLSchema=org.acme.foo</extraarg>
[...]
</plugin>
Нет необходимости в targetNamespace, указывающем на зарезервированное пространство имен XSD, и нет необходимости связывать все пакеты jaxb.
Лучший способ использовать GlobalBinding - это указать явный адаптер вместо использования этой пары синтаксический анализ / печать. Например, вместо следующего:
<jaxb:javaType name="java.lang.Long" xmlType="xs:long"
parseMethod="com.mypackage.myclass.parseLong"
printMethod="com.mypackage.myclass.print"/>
Вместо этого вы должны:
<xjc:javaType name="java.lang.Long" xmlType="xs:long"
adapter="com.mypackage.LongAdapter"/>
Не забудьте добавить пространство имен для xjc:
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
jxb:extensionBindingPrefixes="xjc"
Класс LongAdapter будет выглядеть так:
public class LongAdapter
extends XmlAdapter<String, Long>
{
public Long unmarshal(String value) {
return your_util_class.parseLong(value);
}
public String marshal(Long value) {
return your_util_class.print(value);
}
}
Таким образом, поскольку вы явно указали классы адаптеров, jaxb не будет генерировать адаптеры по умолчанию с именем пакета по умолчанию org.w3._2001.xmlschema.
Очень важно избегать использования имени пакета по умолчанию org.w3._2001.xmlschema. Возьмем один пример, если у вас есть один проект A и один проект B, и у них обоих есть некоторые схемы и привязки. По старинке оба они генерируют адаптеры с одинаковыми полными именами, например, org.w3._2001.xmlschema.Adapter1. Тем не менее, этот адаптер может быть для Long в проекте A и для Integer в проекте B. Тогда, скажем, у вас есть нижестоящий проект C, использующий как A, так и B. Теперь проблема становится неприятной. Если C необходимо использовать Adapter1, вы не можете предсказать, что использованный адаптер - от A для Long или от B для Integer. Тогда ваше приложение C может нормально работать через какое-то время, но может не получиться странным образом в некоторых других ситуациях. Если это произойдет, исключение типа будет выглядеть так:
org.w3._2001.xmlschema.Adapter1 is not applicable to the field type java.lang.Double...
Решение, упомянутое Roy Truelove, кажется, не работает, когда я попробовал его в своей среде с помощью maven-jaxb2-plugin, даже если теория верна.
org.w3._2001.xmlschema
пакет создается здесь, потому что XJC должен генерировать класс, который расширяет javax.xml.bind.annotation.adapters.XmlAdapter
, который в свою очередь вызывает ваши синтаксические разбор / печать статических методов. По какой-то причине он помещает их в этот пакет, а не куда-то более полезный.
Вы не сказали, какую реализацию JAXB вы используете, но RI JAXB имеет расширение к javaType
настройка привязки, которая позволяет указать подкласс XmlAdapter
напрямую, а не parseMethod
/printMethod
пар. Это устраняет необходимость генерировать синтетические XmlAdapter
бридж класс. Посмотрите документы RI для того, как это сделать.
Я предполагаю, что EclipseLink/Moxy имеет что-то похожее на это, но я не уверен, способен ли XJC, который поставляется с Java6, на это (Sun, кажется, удалил половину полезных вещей из RI, когда они принесли его в JRE),
Используйте встроенные конвертеры для распространенных типов данных.
<jxb:javaType name="java.lang.Integer" xmlType="xs:integer"
parseMethod="javax.xml.bind.DatatypeConverter.parseInt"
printMethod="javax.xml.bind.DatatypeConverter.printInt" />