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