如何以编程方式更新复杂类型并将其添加到 XSD
How to Programmatically Update and Add a Complex Type with a Sequence to an 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>
所以我的问题是如何使用相同的 DOM 方法添加一个名为 "Container 3"... 的新复杂类型...具有包含 "element 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));
谢谢!
在我看来,这确实是您应该使用 XSLT 做的事情。 XML 架构是一个 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>
首先,警告一下。如果您想使用 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 命名空间规范。
感谢您的建议。虽然@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));
我正在尝试以编程方式更新 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>
所以我的问题是如何使用相同的 DOM 方法添加一个名为 "Container 3"... 的新复杂类型...具有包含 "element 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));
谢谢!
在我看来,这确实是您应该使用 XSLT 做的事情。 XML 架构是一个 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>
首先,警告一下。如果您想使用 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 命名空间规范。
感谢您的建议。虽然@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));