JAXB, Custom bindings, Adapter1.class and Joda-tim

2019-02-16 12:56发布

I have a problem with the way JAXB is generating the bound classes for an XML schema (which, for sake of precision, I cannot modify). I want to map a xsd:date type to a Joda-time LocalDate object and, reading here, here and here, I created the following DateAdapter class:

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");
    }
}

And I added the following to my global binding file:

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

The problem is that, when I try to maven compile my project, it fails with the following error:

[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

...and this is where things get weird. JAXB generates a class Adapter1 that contains the following:

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));
    }

}

....which is the source of the compilation error.

Now, my questions are:

  • being that my adapter is overriding XmlAdapter, I cannot make the methods static....how do I avoid this?
  • Can I avoid the generation of Adapter1.class altogether?? Maybe using package-level annotation XmlJavaTypeAdapters, and if so, how do I do it exactly?? (JAXB generates already a package-info.java of its own....)

Hope I made my situation clear.
Thanks

7条回答
我想做一个坏孩纸
2楼-- · 2019-02-16 13:23

A complete example. This is your 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>

plus the Java Class:

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);
    }

}

plus the pom.xml definition:

<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>
查看更多
该账号已被封号
3楼-- · 2019-02-16 13:33

You do not need to extend XmlAdapter and with Joda-Time v2, you do not even need to implement static methods, as they are already provided.

<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"
/>

See JAXB datatype converters for xs:date xs:time and xs:dateTime

查看更多
Luminary・发光体
4楼-- · 2019-02-16 13:35

When generating XmlAdapters from an XML schema you need to put the logic for the conversion in static methods not in an XmlAdapter. This is so an XmlAdapter that leverages that logic can be generated. I recognize that this is an odd mechanism.

Complete Example

查看更多
爱情/是我丢掉的垃圾
5楼-- · 2019-02-16 13:38

I found this solution as useful http://blog.bdoughan.com/2011/05/jaxb-and-joda-time-dates-and-times.html

You will create an adapter

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();
    }

}

Then register it with annotations by defining a blog/jodatime/package-info.java in your sources

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

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

import org.joda.time.*;

Then you should expect that the serialization of DateTime is done without any other changes, just don't forget to annotate your class with @XmlRootElement.

查看更多
趁早两清
6楼-- · 2019-02-16 13:42

You do not need to extend XmlAdapter.

Just create static methods on a POJO and it will work.

Example:

 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");
    }
 }
查看更多
贼婆χ
7楼-- · 2019-02-16 13:44

You are extending XmlAdapter which is used when you want to annotation your Java model for JaxB, that is through the annotation @XmlJavaTypeAdapter(Adapter1.class). For your case you just need a class with static methods that does not extend XmlAdapter. You will need the parse method (take a string and return date) and print method (take a date and return a string) and that's about it.

查看更多
登录 后发表回答