如何以编程方式更新具有序列的复杂类型并将其添加到XSD中



我正在尝试用程序更新java中的现有XSD,它看起来像这样:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns="com/company/common" xmlns:xs="http://www.w3.org/2001/XMLSchema"
    targetNamespace="com/company/common/" elementFormDefault="qualified">
    <xs:include schemaLocation="DerivedAttributes.xsd" />
    <xs:element name="MyXSD" type="MyXSD" />
    <xs:complexType name="Container1">
        <xs:sequence>
            <xs:element name="element1" type="element1" minOccurs="0"
                maxOccurs="unbounded" />
            <xs:element name="element2" type="element2" minOccurs="0"
                maxOccurs="unbounded" />
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="Container2">
        <xs:sequence>
            <xs:element name="element3" type="Type1" minOccurs="0" />
            <xs:element name="element4" type="Type2" minOccurs="0" />
        </xs:sequence>
    </xs:complexType>
</xs:schema>

使用DOM和XPath,我可以很容易地将新元素添加到容器1中,如下所示:

Document doc = DocumentBuilderFactory.newInstance()
            .newDocumentBuilder().parse(
                    new InputSource("test.xsd"));
    // use xpath to find node to add to
    XPath xPath = XPathFactory.newInstance().newXPath();
    NodeList nodes = (NodeList) xPath
            .evaluate(
                    "/schema/complexType[@name="Container1"]/sequence",
                    doc.getDocumentElement(), XPathConstants.NODESET);
    // create element to add
    org.w3c.dom.Element newElement = doc.createElement("xs:element");
    newElement.setAttribute("name", "element5");
    newElement.setAttribute("type", "type5");
    newElement.setAttribute("minOccurs", "0");
    newElement.setAttribute("manOccurs", "unbounded");
    nodes.item(0).appendChild(newElement);
    // output
    TransformerFactory.newInstance().newTransformer().transform(
            new DOMSource(doc.getDocumentElement()),
            new StreamResult(System.out));

我能够得到这样的结果:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns="com/company/common" xmlns:xs="http://www.w3.org/2001/XMLSchema"
    targetNamespace="com/company/common/" elementFormDefault="qualified">
    <xs:include schemaLocation="DerivedAttributes.xsd" />
    <xs:element name="MyXSD" type="MyXSD" />
    <xs:complexType name="Container1">
        <xs:sequence>
            <xs:element name="element1" type="element1" minOccurs="0"
                maxOccurs="unbounded" />
            <xs:element name="element2" type="element2" minOccurs="0"
                maxOccurs="unbounded" />
            <xs:element name="element3" type="element2" minOccurs="0"
                maxOccurs="unbounded" />
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="Container2">
        <xs:sequence>
            <xs:element name="element3" type="Type1" minOccurs="0" />
            <xs:element name="element2" type="Type2" minOccurs="0" />
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="Container3">
        <xs:sequence>
            <xs:element name="element4" type="Type1" minOccurs="0" />
        </xs:sequence>
    </xs:complexType>
</xs:schema>

所以我的问题是如何添加一个名为"Container3"的新复杂类型。。。用一个序列。。。它包含使用相同DOM appach的"元素5",所以它看起来像这样:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns="com/company/common" xmlns:xs="http://www.w3.org/2001/XMLSchema"
    targetNamespace="com/company/common/" elementFormDefault="qualified">
    <xs:include schemaLocation="DerivedAttributes.xsd" />
    <xs:element name="MyXSD" type="MyXSD" />
    <xs:complexType name="Container1">
        <xs:sequence>
            <xs:element name="element1" type="element1" minOccurs="0"
                maxOccurs="unbounded" />
            <xs:element name="element2" type="element2" minOccurs="0"
                maxOccurs="unbounded" />
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="Container2">
        <xs:sequence>
            <xs:element name="element3" type="Type1" minOccurs="0" />
            <xs:element name="element4" type="Type2" minOccurs="0" />
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="Container3">
        <xs:sequence>
            <xs:element name="element5" type="Type1" minOccurs="0" />
        </xs:sequence>
    </xs:complexType>
</xs:schema>

现在我正在使用一个DOM解析器,它添加了一个新的复杂类型。。。但我不知道如何创建一个复杂的类型,它也有一个元素的序列。这就是我目前所拥有的。。。

Document doc = DocumentBuilderFactory.newInstance()
            .newDocumentBuilder().parse(
                    new InputSource("test.xsd"));
    // use xpath to find node to add to
    XPath xPath = XPathFactory.newInstance().newXPath();
    NodeList nodes = (NodeList) xPath.evaluate("/schema", doc
            .getDocumentElement(), XPathConstants.NODESET);
    // create element to add
    org.w3c.dom.Element newElement = doc.createElement("xs:complexType");
    newElement.setAttribute("name", "Container3");
    nodes.item(0).appendChild(newElement);
    // output
    TransformerFactory.newInstance().newTransformer().transform(
            new DOMSource(doc.getDocumentElement()),
            new StreamResult(System.out));

谢谢!

首先,一个警告词。如果您想使用XPath,您应该正确处理名称空间-XPath语言只在名称空间格式良好的XML上定义,虽然一些DOM和XPath实现似乎在没有名称空间的情况下解析的DOM树上工作,但这并不能保证,如果您因任何原因换用不同的解析器,事情可能会中断。

考虑到在javax.xml.xpath中使用名称空间是多么麻烦,我倾向于改用更Java友好的对象模型,如dom4j。

尽管在这种情况下实际上根本不需要使用XPath,因为您只是在文档的根元素中添加了一个新的子元素:

// this is org.dom4j.Document, not w3c
SAXReader reader = new SAXReader();
Document doc = reader.read(new File("test.xsd"));
doc.getRootElement()
  .addElement("xs:complexType", "http://www.w3.org/2001/XMLSchema")
    .addAttribute("name", "Container4")
    .addElement("xs:sequence", "http://www.w3.org/2001/XMLSchema")
      .addElement("xs:element", "http://www.w3.org/2001/XMLSchema")
        .addAttribute("name", "element5")
        .addAttribute("type", "xs:string")
        .addAttribute("minOccurs", "0");
System.out.println(doc.asXML());

或者在W3C DOM:中

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
// enable namespaces - for some obscure reason this is false by default
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().parse(
                new InputSource("test.xsd"));
// create element to add
org.w3c.dom.Element newComplexType = doc
        .createElementNS("http://www.w3.org/2001/XMLSchema", "xs:complexType");
org.w3c.dom.Element newSequence = doc
        .createElementNS("http://www.w3.org/2001/XMLSchema", "xs:sequence");
org.w3c.dom.Element newElement = doc
        .createElementNS("http://www.w3.org/2001/XMLSchema", "xs:element");
newComplexType.setAttributeNS(null, "name", "Container3");
newElement.setAttributeNS(null, "name", "element5");
newElement.setAttributeNS(null, "type", "type5");
newElement.setAttributeNS(null, "minOccurs", "0");
newElement.setAttributeNS(null, "manOccurs", "unbounded");
doc.getDocumentElement().appendChild(newComplexType)
        .appendChild(newSequence).appendChild(newElement);
// output
TransformerFactory.newInstance().newTransformer().transform(
        new DOMSource(doc),
        new StreamResult(System.out));

考虑到我们处理的XML涉及名称空间,我们必须使用DOM方法的NS变体,而不是在引入XML名称空间规范之前的古老的非名称空间感知方法。

在我看来,这确实是XSLT应该做的事情。XML模式是一个XML文档,由于您希望将其转换为另一个XML文件,XSLT是实现这一点的完美工具。使用Java和DOM非常复杂(非常主观)。

要执行这个样式表,您需要一个Java中的XSLT处理器,但有很多好的资源可以展示如何做到这一点(这似乎是其中之一)。

XSLT样式表

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xsl:strip-space elements="*"/>
    <xsl:output method="xml" encoding="UTF-8" indent="yes" />
    <xsl:template match="/*">
      <xsl:copy>
        <xsl:apply-templates/>
        <!--Introduce the new element-->
        <xs:complexType name="Container3">
            <xs:sequence>
                <xs:element name="element5" type="Type1" minOccurs="0" />
            </xs:sequence>
        </xs:complexType>
      </xsl:copy>
    </xsl:template>
    <!--Identity template, produces an exact copy of the input-->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:transform>

XML输出

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="com/company/common">
   <xs:include schemaLocation="DerivedAttributes.xsd"/>
   <xs:element name="MyXSD" type="MyXSD"/>
   <xs:complexType name="Container1">
      <xs:sequence>
         <xs:element name="element1" type="element1" minOccurs="0" maxOccurs="unbounded"/>
         <xs:element name="element2" type="element2" minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
   </xs:complexType>
   <xs:complexType name="Container2">
      <xs:sequence>
         <xs:element name="element3" type="Type1" minOccurs="0"/>
         <xs:element name="element4" type="Type2" minOccurs="0"/>
      </xs:sequence>
   </xs:complexType>
   <xs:complexType name="Container3">
      <xs:sequence>
         <xs:element name="element5" type="Type1" minOccurs="0"/>
      </xs:sequence>
   </xs:complexType>
</xs:schema>

感谢您的建议。虽然@Ian-Roberts解决方案在SAX中可能更优雅,但我能够用DOM这样做:

    Document doc = DocumentBuilderFactory.newInstance()
            .newDocumentBuilder().parse(
                    new InputSource("test.xsd"));
    // use xpath to find node to add to
    XPath xPath = XPathFactory.newInstance().newXPath();
    NodeList nodes = (NodeList) xPath.evaluate("/schema", doc
            .getDocumentElement(), XPathConstants.NODESET);
    // create element to add
    org.w3c.dom.Element newComplexType = doc
            .createElement("xs:complexType");
    org.w3c.dom.Element newSequence = doc.createElement("xs:sequence");
    org.w3c.dom.Element newElement = doc.createElement("xs:element");
    newComplexType.setAttribute("name", "Container3");
    newElement.setAttribute("name", "element5");
    newElement.setAttribute("type", "type5");
    newElement.setAttribute("minOccurs", "0");
    newElement.setAttribute("manOccurs", "unbounded");
    nodes.item(0).appendChild(newComplexType).appendChild(newSequence)
            .appendChild(newElement);
    // output
    TransformerFactory.newInstance().newTransformer().transform(
            new DOMSource(doc.getDocumentElement()),
            new StreamResult(System.out));

最新更新