简体   繁体   中英

JAXB class generation with imported XSD and binding

I am trying to generate classes from following common.xsd which imports x.xsd and y.xsd .

common.xsd is as follows:

<?xml version="1.0"?>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:import namespace="mynamespace:x" schemaLocation="x.xsd"/>
    <xs:import namespace="mynamespace:y" schemaLocation="y.xsd"/>
</xs:schema>

I try to use a binding file that specifies a common interface that is to be implemented by the generated classes. My binding file is as follows:

jaxb:extensionBindingPrefixes="inheritance" version="2.1">

<jaxb:globalBindings> 
    <jaxb:javaType name="java.lang.Long" xmlType="xsd:integer"/> 
</jaxb:globalBindings>

<jaxb:bindings schemaLocation="common.xsd" node="/xsd:schema">

    <jaxb:bindings node="xsd:complexType[@name='Customer']">
        <inheritance:implements>jaxb.BaseMessage</inheritance:implements>
        <jaxb:class />
    </jaxb:bindings>

    <jaxb:bindings node="xsd:complexType[@name='Payments']">
        <inheritance:implements>jaxb.BaseMessage</inheritance:implements>
        <jaxb:class />
    </jaxb:bindings>

I tried to generate the code but it complains that:

[ERROR] XPath evaluation of "xsd:complexType[@name='Customer']" results in empty target node
[ERROR] XPath evaluation of "xsd:complexType[@name='Payments']" results in empty target node

How can I define the nodes in the bindings files are actually in the individual external XSD files but not in common.xsd ?

Normally, the way you would want to do this would be to use Schema Component Designators (SCD) instead of XPath.

Using SCD for customizations

[XPath] is also error prone, because it relies on the way schema documents are laid out, because the schemaLocation attribute needs to point to the right schema document file. When a schema is split into multiple files for modularity (happens especially often with large schemas), then you'd have to find which schema file it is. Even though you can use relative paths, this hard-coding of path information makes it hard to pass around the binding file to other people.

SCD Support

Compared to the standard XPath based approach, SCD allows more robust and concise way of identifying a target of a customization. For more about SCD, refer to the scd example. Note that SCD is a W3C working draft, and may change in the future.

Unfortunately, due to a bug in XJC , SCD does not work in combination with vendor extensions. You would see an error like:

[ERROR] cvc-elt.1: Cannot find the declaration of element 'inheritance:implements'.

The author of jaxb2-basics has recently written up a detailed explanation of that particular problem. Basically, if you want to use vendor extensions, you are stuck with XPath (and its limitations) for now.

An XPath-based solution

Here is a complete, working example using XPath with the vendor extensions based on the information you provided in your question. I believe the proper way to generate the classes from the imported schemas are via a separate bindings element. As proof that this is working as expected, the class generated from this binding ( Cust ) is visible and reused by those classes generated by common.xsd . Each generated class implements the base class jaxb.BaseMessage .

I believe this is as good a solution as you will find until the XJC bug is fixed.

src/main/resources/bindings.xjb :

<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    jaxb:version="2.1"
    xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
    xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
    xmlns:inheritance="http://jaxb2-commons.dev.java.net/basic/inheritance"
    jaxb:extensionBindingPrefixes="xjc inheritance">

  <jaxb:globalBindings>
    <jaxb:javaType name="java.lang.Long" xmlType="xsd:integer" />
  </jaxb:globalBindings>

  <jaxb:bindings schemaLocation="schema/x.xsd">
    <jaxb:bindings node="//xsd:complexType[@name='Customer']">
      <jaxb:class name="Cust" />
      <inheritance:implements>jaxb.BaseMessage</inheritance:implements>
    </jaxb:bindings>
  </jaxb:bindings>

  <jaxb:bindings schemaLocation="schema/y.xsd">
    <jaxb:bindings node="//xsd:complexType[@name='Payments']">
      <jaxb:class />
      <inheritance:implements>jaxb.BaseMessage</inheritance:implements>
    </jaxb:bindings>
  </jaxb:bindings>

  <jaxb:bindings schemaLocation="schema/common.xsd">
    <jaxb:bindings node="//xsd:complexType[@name='CustomerPayments']">
      <jaxb:class />
      <inheritance:implements>jaxb.BaseMessage</inheritance:implements>
    </jaxb:bindings>
  </jaxb:bindings>

</jaxb:bindings>

src/main/resources/schema/common.xsd :

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema version="1.0"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:x="http://test.org/common/x"
    xmlns:y="http://test.org/common/y"
    targetNamespace="http://test.org/common">

  <xsd:import namespace="http://test.org/common/x" schemaLocation="x.xsd" />
  <xsd:import namespace="http://test.org/common/y" schemaLocation="y.xsd" />

  <xsd:complexType name="CustomerPayments">
    <xsd:sequence>
      <xsd:element name="customer" type="x:Customer" />
      <xsd:element name="payments" type="y:Payments" />
    </xsd:sequence>
  </xsd:complexType>

</xsd:schema>

src/main/resources/schema/x.xsd :

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema version="1.0"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    elementFormDefault="qualified"
    attributeFormDefault="unqualified"
    targetNamespace="http://test.org/common/x">

  <xsd:complexType name="Customer">
    <xsd:sequence>
      <xsd:element name="name" type="xsd:string" />
    </xsd:sequence>
  </xsd:complexType>

</xsd:schema>

src/main/resources/schema/y.xsd :

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema version="1.0"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    elementFormDefault="qualified"
    attributeFormDefault="unqualified"
    targetNamespace="http://test.org/common/y">

  <xsd:complexType name="Payments">
    <xsd:sequence>
      <xsd:element name="amount" type="xsd:float" />
    </xsd:sequence>
  </xsd:complexType>

</xsd:schema>

build.xml :

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE project>
<project name="JAXB XPath Test" default="xjc" basedir=".">

  <property name="build.folder" location="build" />

  <taskdef name="xjc" classname="org.jvnet.jaxb2_commons.xjc.XJC2Task">
    <classpath>
        <fileset dir="${basedir}/lib">
          <include name="jaxb-impl-2.2.6.jar" />
          <include name="jaxb-xjc-2.2.6.jar" />
          <include name="jaxb2-basics-ant-0.9.4.jar" />
          <include name="javaparser-1.0.11.jar" />
          <include name="commons-lang3-3.2.jar" />
        </fileset>
    </classpath>
  </taskdef>

  <target name="xjc" description="Generate the source code.">

    <xjc destdir="${basedir}/src/main/java" extension="true">

      <arg line="
        -Xequals
        -XhashCode
        -XtoString
        -Xcopyable
        -Xmergeable
        -Xinheritance" />

      <binding dir="${basedir}/src/main/resources">
        <include name="**/*.xjb" />
      </binding>

      <schema dir="${basedir}/src/main/resources/schema">
        <include name="**/*.xsd" />
      </schema>

      <classpath>
        <fileset dir="${basedir}/lib">
            <include name="jaxb2-basics-0.9.4.jar"/>
            <include name="jaxb2-basics-runtime-0.9.4.jar"/>
            <include name="jaxb2-basics-tools-0.9.4.jar"/>
            <include name="commons-beanutils-1.8.0.jar"/>
            <include name="commons-lang3-3.2.jar"/>
            <include name="commons-logging-1.1.1.jar"/>
        </fileset>
      </classpath>

    </xjc>

  </target>

</project>

I'm posting a second answer because I believe what you are trying to achieve is possible without vendor extensions, but I also think that my original answer may be useful to others who need vendor extensions.

This example also removes the namespaces, but they could be added back easily. The same build script is used for this answer as in my previous one.

src/main/resources/bindings.xjb :

<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    jaxb:version="2.1"
    xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
    xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
    jaxb:extensionBindingPrefixes="xjc"
    xmlns:common="schema/common.xsd">

  <jaxb:globalBindings>
    <jaxb:javaType name="java.lang.Long" xmlType="xsd:integer" />
    <xjc:superInterface name="jaxb.BaseMessage" />
  </jaxb:globalBindings>

</jaxb:bindings>

src/main/resources/schema/common.xsd :

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema version="1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

  <xsd:include schemaLocation="x.xsd" />
  <xsd:include schemaLocation="y.xsd" />

  <xsd:complexType name="CustomerPayments">
    <xsd:sequence>
      <xsd:element name="customer" type="Customer" />
      <xsd:element name="payments" type="Payments" />
    </xsd:sequence>
  </xsd:complexType>

</xsd:schema>

src/main/resources/schema/x.xsd :

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema version="1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <xsd:complexType name="Customer">
      <xsd:sequence>
        <xsd:element name="name" type="xsd:string" />
      </xsd:sequence>
    </xsd:complexType>
</xsd:schema>

src/main/resources/y.xsd :

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema version="1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <xsd:complexType name="Payments">
      <xsd:sequence>
        <xsd:element name="amount" type="xsd:float" />
      </xsd:sequence>
    </xsd:complexType>
</xsd:schema>

Why don't you just point your schemaLocation to x.xsd and y.xsd , where the types are defined?

<jaxb:bindings schemaLocation="x.xsd" node="/xsd:schema">
    <jaxb:bindings node="xsd:complexType[@name='Customer']">
        <inheritance:implements>jaxb.BaseMessage</inheritance:implements>
        <jaxb:class />
    </jaxb:bindings>
</jaxb:bindings>

<jaxb:bindings schemaLocation="y.xsd" node="/xsd:schema">
    <jaxb:bindings node="xsd:complexType[@name='Payments']">
        <inheritance:implements>jaxb.BaseMessage</inheritance:implements>
        <jaxb:class />
    </jaxb:bindings>
</jaxb:bindings>

I think it doesn't work because you didn't start the node with //

Try this:

<jaxb:bindings node="//xsd:complexType[@name='Customer']">
    <inheritance:implements>jaxb.BaseMessage</inheritance:implements>
</jaxb:bindings>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM