11

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?

Parker
  • 7,244
  • 12
  • 70
  • 92
user3057702
  • 137
  • 1
  • 2
  • 8
  • I had to fill in some missing pieces in order to produce the test case in my answer. Let me know if I have made any incorrect assumptions about your use case. – Parker Jun 19 '15 at 15:28
  • Hi, I have opened a bounty for this question, but it is not the same problem I had. My problem comes when using wsdl2java and bindings. I have solved the problem with the help of the answers, so I'll award the 50 points to the correct answer to your problem @vallismortis – Rafael Sisto Jun 23 '15 at 18:44
  • sorry, @user3057702 asked the question... Did you find your answer??? – Rafael Sisto Jun 24 '15 at 14:23

4 Answers4

5

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>
Community
  • 1
  • 1
Parker
  • 7,244
  • 12
  • 70
  • 92
  • I have a feedling you're overcomplicating. The problem is that `schemaLocation` points to the wrong schema. I don't see why SCDs were truly necessary here. – lexicore Jun 20 '15 at 05:23
  • 1
    I think there may be part of the use case that we are not seeing here. I made the assumption that the 'common.xml' may be the only schema under the developer's control, and that there could be potentially be many other schemas included, the derived types all defined in the "common" file. The way the question is worded, it seems like it is desirable to not refer directly to all imported schemas. That is one of the cases that scd was designed to ameliorate. Hopefully OP responds and we can see the rest of the use case. – Parker Jun 20 '15 at 09:09
  • Hm, you don't need to "control" schema to be able to apply bindings to it. I've compiled big schema collections with (almost) no SCDs. I'd say we've provided enough hints. Don't expect much reaction from new low-rep users. – lexicore Jun 20 '15 at 13:21
2

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>
Parker
  • 7,244
  • 12
  • 70
  • 92
1

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>
lexicore
  • 42,748
  • 17
  • 132
  • 221
-1

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>
josliber
  • 43,891
  • 12
  • 98
  • 133
Code_Eraser
  • 121
  • 7
  • 2
    In the question, `node="/xsd:schema"` was used as the starting node. Typically, `xsd:complexType[@name='Customer']` would be expected to be a child node of the schema element. However, XPath is not finding them when importing from schemas using their own namespaces. – Parker Jun 19 '15 at 17:20