JAXB, пользовательские привязки, Adapter1.class и Joda-time

У меня возникла проблема с тем, как JAXB создает связанные классы для XML-схемы (которая, ради точности, я не могу изменить). Я хочу сопоставить xsd: тип даты с объектом LocalDate для Joda-времени и, прочитав здесь, здесь и здесь, я создал следующий класс DateAdapter:

public class DateAdapter extends XmlAdapter<String,LocalDate> {
    private static DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyyMMdd");

    public LocalDate unmarshal(String v) throws Exception {
        return fmt.parseLocalDate(v);
    }

    public String marshal(LocalDate v) throws Exception {
        return v.toString("yyyyMMdd");
    }
}

И я добавил в свой глобальный файл привязки следующее:

  <jaxb:globalBindings>
        <jaxb:javaType name="org.joda.time.LocalDate" xmlType="xs:date"
            parseMethod="my.classes.adapters.DateAdapter.unmarshal"
            printMethod="my.classes.adapters.DateAdapter.marshal" />
    </jaxb:globalBindings>

Проблема в том, что когда я пытаюсь скомпилировать мой проект maven, он терпит неудачу со следующей ошибкой:

[ERROR] \My\Path\MyProject\target\generated-sources\xjc\my\classes\generated\Adapter1.java:[20,59] non-static method unmarshal(java.lang.String) cannot be referenced from a static context
[ERROR] \My\Path\MyProject\target\generated-sources\xjc\my\classes\generated\Adapter1.java:[24,59] non-static method marshal(org.joda.time.LocalDate) cannot be referenced from a static context

... и здесь все становится странно. JAXB создает класс Adapter1, который содержит следующее:

public class Adapter1
    extends XmlAdapter<String, LocalDate>
{


    public LocalDate unmarshal(String value) {
        return (my.classes.adapters.DateAdapter.unmarshal(value));
    }

    public String marshal(LocalDate value) {
        return (my.classes.adapters.DateAdapter.marshal(value));
    }

}

.... который является источником ошибки компиляции.

Теперь мои вопросы:

  • то, что мой адаптер переопределяет XmlAdapter, я не могу сделать методы статическими.... как этого избежать?
  • Можно ли вообще избежать создания класса Adapter1.class? Возможно, используя аннотацию на уровне пакета XmlJavaTypeAdapters, и если да, то как мне это сделать точно? (JAXB генерирует уже собственный package.info.java.)

Надеюсь, я сделал свою ситуацию ясной.
Благодаря

Ответ 1

Вам не нужно расширять XmlAdapter.

Просто создайте статические методы на POJO, и он будет работать.

Пример:

 public class DateAdapter {
    private static DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyyMMdd");

    public static LocalDate unmarshal(String v) throws Exception {
        return fmt.parseLocalDate(v);
    }

    public static String marshal(LocalDate v) throws Exception {
        return v.toString("yyyyMMdd");
    }
 }

Ответ 2

Я был в первом контексте WSDL: вообще нет java, просто создайте клиент CXF из предоставленного WSDL.

Я долго задерживался с уродливым Adapter1.java, но я нашел решение там.

Вы будете использовать пользовательский XMLAdapter, как уже объяснено.

Ключом этой проблемы было добавление расширения xjc в глобальный файл привязки:

<jaxb:bindings xmlns:jaxb="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"
 jaxb:extensionBindingPrefixes="xjc" jaxb:version="2.1">
    <jaxb:globalBindings>
       <xjc:javaType adapter="com.xxx.tools.xjc.DateAdapter" 
        name="java.util.Date" xmlType="xs:dateTime" />
    </jaxb:globalBindings>
</jaxb:bindings>
Расширение

xjc позволяет использовать xjc:javaType, который принимает параметр адаптер. Больше не требуется статический метод!

Примечание, похоже, работает только с jaxb 2.1+.

Ответ 3

При создании XmlAdapters из схемы XML вам нужно поставить логику для преобразования в статических методах не в XmlAdapter. Это значит, что XmlAdapter, который использует эту логику, может быть сгенерирован. Я понимаю, что это нечетный механизм.

Полный пример

Ответ 4

Вы расширяете XmlAdapter, который используется, когда вы хотите аннотировать вашу Java-модель для JaxB, то есть через аннотацию @XmlJavaTypeAdapter(Adapter1.class). Для вашего случая вам просто нужен класс со статическими методами, который не распространяется на XmlAdapter. Вам понадобится метод parse (взять строку и дату возврата) и метод печати (взять дату и вернуть строку), и об этом.

Ответ 5

Я нашел это решение полезным http://blog.bdoughan.com/2011/05/jaxb-and-joda-time-dates-and-times.html

Вы создадите адаптер

package blog.jodatime;

import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.joda.time.DateTime;

public class DateTimeAdapter 
    extends XmlAdapter<String, DateTime>{

    public DateTime unmarshal(String v) throws Exception {
        //return new DateTime(v); - old solution that didn't properly handled the timezone
        return DateTime.parse(v);
    }

    public String marshal(DateTime v) throws Exception {
        return v.toString();
    }

}

Затем зарегистрируйте его с помощью аннотаций, указав в своих источниках блог /jodatime/package -info.java

@XmlJavaTypeAdapters({ @XmlJavaTypeAdapter(type = DateTime.class, value = JodaDateTimeJaxbAdapter.class) })
package blog.jodatime;

import javax.xml.bind.annotation.adapters.*;

import org.joda.time.*;

Тогда вы должны ожидать, что сериализация DateTime будет выполнена без каких-либо изменений, просто не забудьте комментировать ваш класс с помощью @XmlRootElement.

Ответ 6

Полный пример. Это ваш bindings.xml:

<jaxws:bindings wsdlLocation="YourWsdl"
      xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
      xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
      xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
      jxb:extensionBindingPrefixes="xjc">
    <jaxws:bindings  node="wsdl:definitions/wsdl:types/xs:schema[@targetNamespace='YourTargetNameSpace']">
       <jxb:globalBindings xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema">
          <xjc:javaType adapter="com.xxx.your.package.DateAdapter" name="java.util.Date" xmlType="xs:dateTime" />
       </jxb:globalBindings>
    </jaxws:bindings>
</jaxws:bindings>

плюс класс Java:

package com.yourpackage;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import javax.xml.bind.DatatypeConverter;
import javax.xml.bind.annotation.adapters.XmlAdapter;

public class DateAdapter extends XmlAdapter<String, Date>
{

    @Override
    public Date unmarshal(final String date) {
        return DatatypeConverter.parseDate(date).getTime();
    }

    @Override
    public String marshal(final Date date)
    {
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(date);
        return DatatypeConverter.printDate(calendar);
    }

}

плюс определение pom.xml:

<plugin>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-codegen-plugin</artifactId>
    <version>${cxf.version}</version>
    <dependencies>
        <dependency>
            <groupId>org.jvnet.jaxb2_commons</groupId>
            <artifactId>jaxb2-basics</artifactId>
            <version>0.6.4</version>
        </dependency>
    </dependencies>
    <executions>
        <execution>
            <id>generate-sources</id>
            <phase>generate-sources</phase>
            <configuration>
                <sourceRoot>${project.build.directory}/generated/cxf</sourceRoot>
                <defaultOptions>
                    <autoNameResolution>true</autoNameResolution>
                </defaultOptions>
                <wsdlOptions>
                    <wsdlOption>
                        <wsdl>${basedir}/src/main/resources/your.wsdl</wsdl>
                        <extraargs>
                            <extraarg>-verbose</extraarg>
                            <extraargs>-xjc-npa</extraargs>
                            <extraarg>-xjc-Xsetters</extraarg>
                        </extraargs>
                        <bindingFiles>
                        <bindingFile>${basedir}/src/main/resources/binding.xml</bindingFile>
                        </bindingFiles>
                    </wsdlOption>
                </wsdlOptions>
            </configuration>
            <goals>
                <goal>wsdl2java</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Ответ 7

Вам не нужно расширять XmlAdapter и с Joda-Time v2, вам даже не нужно реализовывать статические методы, поскольку они уже предоставлены.

<jaxb:javaType xmlns="http://java.sun.com/xml/ns/jaxb" 
    name="org.joda.time.LocalDate"
    xmlType="xs:date"
    parseMethod="org.joda.time.LocalDate.parse"
    printMethod="java.lang.String.valueOf"
/>

См. Преобразователи данных JAXB для xs: date xs: time и xs: dateTime