Файл привязки JAXB: XmlAdapters и имя пакета - PullRequest
15 голосов
/ 17 марта 2011

У меня есть такой файл привязки

<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 ]

11 голосов
/ 17 марта 2011

Здесь создается пакет org.w3._2001.xmlschema, потому что XJC должен сгенерировать класс, который расширяет javax.xml.bind.annotation.adapters.XmlAdapter, который, в свою очередь, вызывает ваши статические методы анализа / печати.По какой-то причине он помещает их в этот пакет, а не куда-нибудь полезнее.

Вы не сказали, какую реализацию JAXB вы используете, но RI JAXB имеет расширение для настройки привязки javaType, которая позволяетВы должны указать подкласс XmlAdapter напрямую, а не parseMethod / printMethod пар.Это избавляет от необходимости генерировать синтетический класс XmlAdapter bridge.См. RI docs , чтобы узнать, как это сделать.

Я полагаю, что EclipseLink / Moxy имеет нечто подобное, но я не уверен, что XJC, который поставляется с Java6, способен(Похоже, что Sun удалила половину полезного материала из RI, когда они привезли его в JRE).

10 голосов
/ 13 февраля 2014

Для пользователей 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.

7 голосов
/ 30 августа 2011

У меня тоже была эта проблема, я решил ее, используя this .

Основная предпосылка заключается в том, что вы включили в компиляцию XJC схему следующего содержания:

<schema xmlns="http://www.w3.org/2001/XMLSchema"
  targetNamespace="http://www.w3.org/2001/XMLSchema"
  xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
  jaxb:version="2.0">
  <annotation><appinfo>
    <jaxb:schemaBindings>
      <jaxb:package name="org.acme.foo"/>
    </jaxb:schemaBindings>
  </appinfo></annotation>
</schema>

Затем вы настраиваете имя пакета в том месте, где вы хотите разместить сгенерированные адаптеры.XJC будет полагать, что эта схема является частью схемы, установленной для самой XML-схемы W3C, и будет соблюдать привязки в ней.

5 голосов
/ 26 августа 2016

Лучший способ использовать 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...

Решение, упомянутое Роем Truelove, кажется, не работает, когда я попробовал его в своей среде с помощью maven-jaxb2-plugin, даже если теория верна.1018 *

0 голосов
/ 27 июня 2018

Используйте встроенные преобразователи для распространенных типов данных.

<jxb:javaType name="java.lang.Integer" xmlType="xs:integer"              
parseMethod="javax.xml.bind.DatatypeConverter.parseInt"                  
printMethod="javax.xml.bind.DatatypeConverter.printInt" />
...