【问题标题】:JAXB class generation with imported XSD and binding使用导入的 XSD 和绑定生成 JAXB 类
【发布时间】:2015-06-28 02:03:49
【问题描述】:

我正在尝试从导入 x.xsdy.xsdcommon.xsd 生成类。

common.xsd如下:

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

我尝试使用一个绑定文件,该文件指定一个由生成的类实现的通用接口。我的绑定文件如下:

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>

我尝试生成代码,但它抱怨:

[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

如何定义绑定文件中的节点实际上是在单个外部 XSD 文件中而不是在 common.xsd 中?

【问题讨论】:

  • 我必须填写一些缺失的部分才能在我的答案中生成测试用例。如果我对您的用例做出任何不正确的假设,请告诉我。
  • 嗨,我已经为这个问题开了一个赏金,但这与我遇到的问题不同。我的问题是在使用 wsdl2java 和绑定时出现的。我已经在答案的帮助下解决了问题,所以我会奖励 50 分给你的问题的正确答案@vallismortis
  • 抱歉,@user3057702 提出了这个问题...你找到答案了吗???

标签: java xml binding xsd jaxb


【解决方案1】:

通常,您希望这样做的方式是使用 Schema Component Designators (SCD) 而不是 XPath。

Using SCD for customizations

[XPath] 也容易出错,因为它依赖于模式文档的布局方式,因为 schemaLocation 属性需要指向正确的模式文档文件。当一个模式被拆分为多个文件以实现模块化时(尤其经常发生在大型模式中),那么您必须找到它是哪个模式文件。即使您可以使用相对路径,这种路径信息的硬编码也使得很难将绑定文件传递给其他人。

SCD Support

与标准的基于 XPath 的方法相比,SCD 允许以更健壮和简洁的方式识别自定义目标。有关 SCD 的更多信息,请参阅 scd 示例。请注意,SCD 是 W3C 工作草案,将来可能会更改。

很遗憾,由于bug in XJC,SCD 不能与供应商扩展结合使用。您会看到如下错误:

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

author of jaxb2-basics 最近针对该特定问题撰写了detailed explanation。基本上,如果您想使用供应商扩展,那么您现在只能使用 XPath(及其局限性)。

基于 XPath 的解决方案

根据您在问题中提供的信息,这是一个使用 XPath 和供应商扩展的完整工作示例。我相信从导入的模式生成类的正确方法是通过单独的绑定元素。为了证明这是按预期工作的,从此绑定 (Cust) 生成的类是可见的,并且可以被common.xsd 生成的那些类重用。每个生成的类都实现基类jaxb.BaseMessage

我相信在 XJC 错误修复之前,这是一个很好的解决方案。

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>

【讨论】:

  • 我有一个你过于复杂的饲料。问题是schemaLocation 指向错误的模式。我不明白为什么这里真的需要 SCD。
  • 我认为可能有一部分用例我们在这里没有看到。我假设“common.xml”可能是开发人员控制下的唯一模式,并且可能包含许多其他模式,派生类型都在“通用”文件中定义。问题的措辞方式,似乎最好不要直接引用所有导入的模式。这是 scd 旨在改善的情况之一。希望 OP 做出回应,我们可以看到其余的用例。
  • 嗯,您无需“控制”模式即可对其应用绑定。我已经编译了(几乎)没有 SCD 的大型模式集合。我想说我们已经提供了足够的提示。不要期望新的低代表用户有太多反应。
【解决方案2】:

我发布第二个答案是因为我相信您尝试实现的目标在没有供应商扩展的情况下是可能的,但我也认为我的原始答案可能对需要供应商扩展的其他人有用。

此示例还删除了命名空间,但可以轻松地将它们添加回来。此答案使用与我之前的答案相同的构建脚本。

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>

【讨论】:

    【解决方案3】:

    为什么不直接将schemaLocation 指向定义类型的x.xsdy.xsd

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

    【讨论】:

      【解决方案4】:

      我认为它不起作用,因为您没有使用 // 启动节点

      试试这个:

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

      【讨论】:

      • 问题中以node="/xsd:schema"作为起始节点。通常,xsd:complexType[@name='Customer'] 应该是模式元素的子节点。但是,当使用自己的命名空间从模式导入时,XPath 没有找到它们。
      猜你喜欢
      • 2020-06-27
      • 2015-06-08
      • 1970-01-01
      • 2012-07-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多