【问题标题】:jaxbcontext generating incomplete schemas?jaxbcontext 生成不完整的模式?
【发布时间】:2014-02-27 22:53:31
【问题描述】:

我在使用 JAXB 时遇到了一个奇怪的问题。我使用 xjc 从我的 XSD 生成我的 java 类,一切看起来都不错。如果我使用 schemagen,它会生成一个与我的原始 xsd 匹配的正确模式。但是,如果我使用 JAXBContext.generateSchema(),那么生成的架构是不完整的。

我使用 Oracle Java 1.6.0_29 和 jaxb-2.2.4-1.jar 作为实现。我附上了 java 代码(生成模式),下面的 xsd 以及 jaxb 调用的输出。

CalculateBorrowingDataResponse.xsd:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema 
    version="1.1"
    attributeFormDefault="unqualified" 
    elementFormDefault="qualified"
    targetNamespace="http://www.domain.com/ClientServices/LendingSimulation/CalculateBorrowingDataResponse" 
    xmlns:lssSt="http://www.domain.com/ClientServices/LendingSimulation/CalculateBorrowingDataResponse"
    xmlns:cbdRes="http://www.domain.com/ClientServices/LendingSimulation/CalculateBorrowingDataResponse" 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">


    <!-- CalculateBorrowingData -->
    <xsd:complexType name="CalculateBorrowingDataResponseType">
        <xsd:sequence>
            <xsd:element name="loanAgmt" type="cbdRes:LoanAgreementType" minOccurs="1" maxOccurs="1" />
        </xsd:sequence>
    </xsd:complexType>


    <xsd:complexType name="LoanAgreementType">
        <xsd:sequence>
            <xsd:element name="borrowingBasedPmtAmt" type="lssSt:borrowingBasedPmtAmt" minOccurs="0" maxOccurs="1" />
            <xsd:element name="maxPmtAmt" type="lssSt:maxPmtAmt" minOccurs="0" maxOccurs="1" />
            <xsd:element name="borrowingCapacityMin" type="lssSt:borrowingCapacityMin" minOccurs="0" maxOccurs="1" />
            <xsd:element name="borrowingCapacityMax" type="lssSt:borrowingCapacityMax" minOccurs="0" maxOccurs="1" />
            <xsd:element name="propertyValueMinAmt" type="lssSt:propertyValueMinAmt" minOccurs="0" maxOccurs="1" />
            <xsd:element name="propertyValueMaxAmt" type="lssSt:propertyValueMaxAmt" minOccurs="0" maxOccurs="1" />
        </xsd:sequence>
    </xsd:complexType>

    <xsd:element name="calculateBorrowingDataResponse" type="cbdRes:CalculateBorrowingDataResponseType"/>


    <xsd:simpleType name="borrowingBasedPmtAmt">
        <xsd:restriction base="xsd:decimal" >
        <xsd:totalDigits value="19" />
        <xsd:fractionDigits value="4" />
        </xsd:restriction>
    </xsd:simpleType>
    <xsd:simpleType name="maxPmtAmt">
        <xsd:restriction base="xsd:decimal" >
        <xsd:totalDigits value="19" />
        <xsd:fractionDigits value="4" />
        </xsd:restriction>
    </xsd:simpleType>
    <xsd:simpleType name="borrowingCapacityMin">
        <xsd:restriction base="xsd:decimal" >
        <xsd:totalDigits value="19" />
        <xsd:fractionDigits value="4" />
        </xsd:restriction>
    </xsd:simpleType>
    <xsd:simpleType name="borrowingCapacityMax">
        <xsd:restriction base="xsd:decimal" >
        <xsd:totalDigits value="19" />
        <xsd:fractionDigits value="4" />
        </xsd:restriction>
    </xsd:simpleType>
    <xsd:simpleType name="propertyValueMinAmt">
        <xsd:restriction base="xsd:decimal" >
        <xsd:totalDigits value="19" />
        <xsd:fractionDigits value="4" />
        </xsd:restriction>
    </xsd:simpleType>
    <xsd:simpleType name="propertyValueMaxAmt">
        <xsd:restriction base="xsd:decimal" >
        <xsd:totalDigits value="19" />
        <xsd:fractionDigits value="4" />
        </xsd:restriction>
    </xsd:simpleType>

</xsd:schema>

Java 代码:

    // Creating the XML tree
    JAXBContext jc = JAXBContext.newInstance( CalculateBorrowingDataResponseType.class );
    Unmarshaller u = jc.createUnmarshaller();

    // generate the schemas
    final List<ByteArrayOutputStream> schemaStreams = new ArrayList<ByteArrayOutputStream>();
    jc.generateSchema(new SchemaOutputResolver(){
        @Override
        public Result createOutput(String namespaceUri, String suggestedFileName) throws IOException {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            schemaStreams.add(out);
            StreamResult streamResult = new StreamResult(out);
            streamResult.setSystemId("");
            return streamResult;
        }});

    // convert to a list of string
    List<String> schemas = new ArrayList<String>();
    for( ByteArrayOutputStream os : schemaStreams )
    {
        schemas.add(os.toString());
        System.out.println( os.toString());
    }

jaxbContext.generateSchema() 的输出:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema elementFormDefault="qualified" version="1.0" targetNamespace="http://www.domain.com/ClientServices/LendingSimulation/CalculateBorrowingDataResponse" xmlns:tns="http://www.domain.com/ClientServices/LendingSimulation/CalculateBorrowingDataResponse" xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:complexType name="CalculateBorrowingDataResponseType">
    <xs:sequence>
      <xs:element name="loanAgmt" type="tns:LoanAgreementType"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="LoanAgreementType">
    <xs:sequence>
      <xs:element name="borrowingBasedPmtAmt" type="xs:decimal" minOccurs="0"/>
      <xs:element name="maxPmtAmt" type="xs:decimal" minOccurs="0"/>
      <xs:element name="borrowingCapacityMin" type="xs:decimal" minOccurs="0"/>
      <xs:element name="borrowingCapacityMax" type="xs:decimal" minOccurs="0"/>
      <xs:element name="propertyValueMinAmt" type="xs:decimal" minOccurs="0"/>
      <xs:element name="propertyValueMaxAmt" type="xs:decimal" minOccurs="0"/>
    </xs:sequence>
  </xs:complexType>
</xs:schema>

如您所见,输出模式非常匹配,除了缺少 calculateBorrowingDataResponse 的元素定义!但是,如果我使用 schemagen, 生成calculateBorrowingDataResponse 元素。

我是否在 SchemaOutputResolver 设置中遗漏了某些内容,或者做了一些不正确/不完整的事情?或者这是 Jaxb RI 中的错误?

【问题讨论】:

    标签: xml jaxb schema


    【解决方案1】:

    改变

    JAXBContext.newInstance(CalculateBorrowingDataResponseType.class);
    

    JAXBContext.newInstance(CalculateBorrowingDataResponseType.class.getPackage().getName());
    

    JAXB 需要在上述类的包内的package-info.java 文件(由xjc 从您的XSD 生成)中定义的信息。

    它不会创建有问题的 XSD 元素,因为 CalculateBorrowingDataResponseType 没有 @XmlRootElement 注释。

    xjc 为什么不从一开始就创建一个?见the most authoritative explanation on the Internets regarding this matter

    如果您向JAXBContext.newInstance(...) 提供包名称,即使CalculateBorrowingDataResponseType 仍然缺少@XmlRootAnnotation,为什么您的代码会生成上述元素? 我一点儿也不知道!Now I have!

    【讨论】:

    • 太棒了!感谢您的快速响应。即使我多次阅读这些文档,我仍然希望 JAXB 会扫描包含的包以查找引用的 ObjectFactory() 和包信息文件。将其更改为包名称就像宣传的那样。
    • +1 - 我还添加了一个答案,解释是 ObjectFactory 类负责导致在 XML 模式中生成该元素。您的方法是执行此操作的一种方法,另一种是将 ObjectFactory 包含在用于引导 JAXBContext 的类中:stackoverflow.com/a/8810207/383861
    • @BlaiseDoughan 我不想在JAXBContext.newInstance(...) 中包含ObjectFactory 类,因为如果我有多个XJC 生成的包,那么它们之间就会存在命名空间冲突。 +1 查找和解释!
    【解决方案2】:

    生成的ObjectFactory 类中的以下内容是导致生成该元素的原因:

    @XmlElementDecl(namespace = "http://www.domain.com/ClientServices/LendingSimulation/CalculateBorrowingDataResponse", name = "calculateBorrowingDataResponse")
    public JAXBElement<CalculateBorrowingDataResponseType> createCalculateBorrowingDataResponse(CalculateBorrowingDataResponseType value) {
        return new JAXBElement<CalculateBorrowingDataResponseType>(_CalculateBorrowingDataResponse_QNAME, CalculateBorrowingDataResponseType.class, null, value);
    }
    

    为了让 JAXBContext 处理 ObjectFactory 类,您需要包含在传递的类数组中以创建 JAXBContext:

    JAXBContext jc = JAXBContext.newInstance(CalculateBorrowingDataResponseType.class, ObjectFactory.class);
    

    或者在生成的类的包名上创建JAXBContext:

    String contextPath = CalculateBorrowingDataResponseType.class.getPackage().getName();
    JAXBContext jc = JAXBContext.newInstance(contextPath);
    

    完整示例

    package forum8809406;
    
    import java.io.IOException;
    
    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.SchemaOutputResolver;
    import javax.xml.transform.Result;
    import javax.xml.transform.stream.StreamResult;
    
    public class Demo {
    
        public static void main(String[] args) throws Exception {
            JAXBContext jc = JAXBContext.newInstance("forum8809406");
            jc.generateSchema(new SOR());
        }
    
        private static class SOR extends SchemaOutputResolver {
    
            @Override
            public Result createOutput(String namespaceUri, String suggestedFileName) throws IOException {
                StreamResult result = new StreamResult(System.out);
                result.setSystemId(suggestedFileName);
                return result;
            }
    
        }
    
    }
    

    我得到以下生成的 XML 架构,其中包含 calculateBorrowingDataResponse 元素:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <xs:schema 
        version="1.0" 
        elementFormDefault="qualified" 
        targetNamespace="http://www.domain.com/ClientServices/LendingSimulation/CalculateBorrowingDataResponse" 
        xmlns:tns="http://www.domain.com/ClientServices/LendingSimulation/CalculateBorrowingDataResponse" 
        xmlns:xs="http://www.w3.org/2001/XMLSchema">
    
      <xs:complexType name="CalculateBorrowingDataResponseType">
        <xs:sequence>
          <xs:element name="loanAgmt" type="tns:LoanAgreementType"/>
        </xs:sequence>
      </xs:complexType>
      <xs:complexType name="LoanAgreementType">
        <xs:sequence>
          <xs:element name="borrowingBasedPmtAmt" type="xs:decimal" minOccurs="0"/>
          <xs:element name="maxPmtAmt" type="xs:decimal" minOccurs="0"/>
          <xs:element name="borrowingCapacityMin" type="xs:decimal" minOccurs="0"/>
          <xs:element name="borrowingCapacityMax" type="xs:decimal" minOccurs="0"/>
          <xs:element name="propertyValueMinAmt" type="xs:decimal" minOccurs="0"/>
          <xs:element name="propertyValueMaxAmt" type="xs:decimal" minOccurs="0"/>
        </xs:sequence>
      </xs:complexType>
    
      <xs:element name="calculateBorrowingDataResponse" type="tns:CalculateBorrowingDataResponseType"/>
    </xs:schema>
    

    【讨论】:

      【解决方案3】:

      为什么你认为在roundrip 'schema1->XJC->schema2' schema1 和 schema 2 应该是相同的?您是否有 XJC 为“calculateBorrowingDataResponse”元素生成的 Java 类?我不这么认为——那么你希望它如何出现在生成的模式中?

      【讨论】:

      • 你有 XJC 为 'calculateBorrowingDataResponse' 元素生成的 Java 类吗? 他确实有 CalculateBorrowingDataResponseType.class,因为他第一次编译 CalculateBorrowingDataResponse.xsdxjc。我看不出从模式生成类,然后从生成的类生成模式的意义,但我该判断谁?
      • 嗯,Type 和 Element 不同。他确实有 Type 类,但没有来自 schema 的 Element 类。
      • @maximdim。不——你是对的。我没有生成元素的 java 类。但是,如果 schemagen 可以从 CalculateBorrowingDataResponseType.class 文件中生成适当的 xsd,我自然认为 jaxb.generateSchema() 也可以。
      • @Kohanyi。从模式生成类然后在运行时生成模式的目的是避免在运行时需要模式(和文件 IO 操作)。像这样,Java 类是在编译时创建的,而在运行时,不再需要模式文件。鉴于动态生成模式有一定的成本,我计划缓存它们,以便只有一次性能损失(以内存为代价)。当然,也可以对 xsd 文件执行相同的操作,但这需要将 xsd 作为类路径的一部分。
      • @maximdim 是的,确实如此,但是如果您检查我的答案,那么您可以看到,如果我使用包名创建 JAXBContext,JAXB 可以以某种方式创建一个元素。不过,关于类型不是元素,你是对的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多