Replace XMLGregorianCalendar via custom binding when using WSDL to Java (wsdl2java)

857 Views Asked by At

My goal is to replace XMLGregorianCalendar by a different Java class (e.g. java.time.LocalDate).

I'm running wsdl2java as part of a maven pom.xml file:

<plugin>
  <groupId>org.apache.cxf</groupId>
  <artifactId>cxf-codegen-plugin</artifactId>
  <version>4.0.2</version>
  <executions>
    <execution>
      <id>generate-sources</id>
      <phase>generate-sources</phase>
      <configuration>
        <sourceRoot>${project.build.directory}/generated-sources/cxf</sourceRoot>
        <wsdlOptions>
          <wsdlOption>
            <wsdl>/path/to/the.wsdl</wsdl>
            <bindingFiles>
              <bindingFile>/path/to/binding.xml</bindingFile>
            </bindingFiles>      
            <extraargs>
              <extraarg>-verbose</extraarg>
            </extraargs>          
          </wsdlOption>
        </wsdlOptions>
      </configuration>
      <goals>
        <goal>wsdl2java</goal>
      </goals>
    </execution>
  </executions>
</plugin>

The binding file looks as follows:

<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
               xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
               xmlns:xs="http://www.w3.org/2001/XMLSchema"
               jaxb:version="2.1">    
    <jaxb:globalBindings>
      <xjc:javaType name="java.time.LocalDate"
                     xmlType="xs:dateTime"
                     adapter="my.app.LocalDateAdapter" />
    </jaxb:globalBindings>
</jaxb:bindings>

However, the generated Java file still uses XMLGregorianCalendar (which is the default class) for dateTime XML types:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "SomeType", propOrder = {
    "someDate"
})
public class SomeType {
    @XmlElement(required = true)
    @XmlSchemaType(name = "dateTime")
    protected XMLGregorianCalendar someDate;
    ...
}

I was expecting the generated Java code to use java.time.LocalDate (instead of XMLGregorianCalendar) for dateTime XML types.

I have seen so many different variations of the binding file but could not get one of them to work. The one binding file mentioned above is the most promising (but obviously wrong) version I came up with.

Although verbose logging is enabled (<extraarg>-verbose</extraarg>), none of the wsdl2java log messages indicate an issue in the binding file. Whatever I try, it feels like it is completely ignoring the binding file.

Any idea about what I'm doing wrong?

3

There are 3 best solutions below

2
Vikash Roy On

To make sure the generated Java code uses java.time.LocalDate instead of XMLGregorianCalendar for dateTime XML types, you need to configure the cxf-codegen-plugin to use the JAXB binding file during code generation. It seems like the binding file is not being picked up currently. You can modify the configuration of the plugin in your pom.xml as follows:

<plugin>
  <groupId>org.apache.cxf</groupId>
  <artifactId>cxf-codegen-plugin</artifactId>
  <version>4.0.2</version>
  <executions>
    <execution>
      <id>generate-sources</id>
      <phase>generate-sources</phase>
      <configuration>
        <sourceRoot>${project.build.directory}/generated-sources/cxf</sourceRoot>
        <wsdlOptions>
          <wsdlOption>
            <wsdl>/path/to/the.wsdl</wsdl>
            <!-- Add the binding file to the configuration -->
            <bindingFiles>
              <bindingFile>/path/to/binding.xml</bindingFile>
            </bindingFiles>
            <extraargs>
              <extraarg>-verbose</extraarg>
            </extraargs>          
          </wsdlOption>
        </wsdlOptions>
        <!-- Add this configuration to use Java 8 Date/Time API -->
        <defaultOptions>
          <java8>true</java8>
        </defaultOptions>
      </configuration>
      <goals>
        <goal>wsdl2java</goal>
      </goals>
    </execution>
  </executions>
  <!-- Add this configuration to use Java 8 Date/Time API -->
  <dependencies>
    <dependency>
      <groupId>javax.xml.bind</groupId>
      <artifactId>jaxb-api</artifactId>
      <version>2.3.1</version>
    </dependency>
  </dependencies>
</plugin>

Make sure to replace /path/to/binding.xml with the actual path to your binding file.

With this configuration, the cxf-codegen-plugin should now use the specified binding file during code generation, and the generated Java classes will use java.time.LocalDate instead of XMLGregorianCalendar for dateTime XML types. Additionally, the true option enables the plugin to generate Java 8 Date/Time API classes in the generated code.

Remember to have the javax.xml.bind:jaxb-api dependency in your pom.xml to ensure that the Java 8 Date/Time API is used properly.

1
JEngineer On
Try using parseMethod of jakarta namespace xmlns:jaxb="https://jakarta.ee/xml/ns/jaxb"

<jaxb:globalBindings>
 <jaxb:javaType name="java.util.Date"  xmlType="xs:date" hasNsContext="false"
        parseMethod="com.example.sandbox.jaxb.DateAdapter.parseDate"
        printMethod="com.example.sandbox.jaxb.DateAdapter.printDate"/>
</jaxb:globalBindings>

import java.util.GregorianCalendar;
import javax.xml.bind.DatatypeConverter;

public class DateAdapter {
 
    public static Date parseDate(String s) {
        return DatatypeConverter.parseDate(s).getTime();
      }
    
public static String printDate(Date dt) {
        Calendar cal = new GregorianCalendar();
        cal.setTime(dt);
        return DatatypeConverter.printDate(cal);
      } 
 }
1
Maksim Eliseev On

I found a working solution. It took me a while.

The bindings file looks like this:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<jaxb:bindings version="3.0"
               xmlns:jaxb="https://jakarta.ee/xml/ns/jaxb"
               xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
               xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <jaxb:globalBindings>
        <xjc:javaType name="java.time.LocalDate"
                      xmlType="xs:dateTime"
                      adapter="my.app.LocalDateAdapter"/>
    </jaxb:globalBindings>
</jaxb:bindings>

The difference with your file is only one line:

  • yours: xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
  • mine: xmlns:jaxb="https://jakarta.ee/xml/ns/jaxb"

Just another namespace.

To confirm this, I posted an example on my github. Let me know if it doesn't work.