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

时间:2015-02-19 15:34:45

标签: java xml dom xpath 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非常轻松地向Container 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>

所以我的问题是如何添加一个名为&#34;容器3&#34; ...的新复杂类型,其中包含序列...包含&#34;元素5&#34;使用相同的DOM apprach,看起来像这样:

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

谢谢!

3 个答案:

答案 0 :(得分:4)

首先,一句警告。如果你想使用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命名空间规范之前的古老的非命名空间感知方法。

答案 1 :(得分:0)

在我看来,你应该用XSLT做些什么。 XML Schema是一个XML文档 - 由于您希望将其转换为另一个XML文档,因此XSLT是完美的工具。使用Java和DOM是无穷无尽的复杂(非常主观)。

要执行此样式表,您需要一个Java中的XSLT处理器,但有许多好的资源显示如何执行此操作(this似乎是其中之一)。

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>

答案 2 :(得分:-1)

感谢您的建议。虽然@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));