6

我用 JAXP 创建了一个 xml 文档并搜索了一种插入模式位置的方法。目前我的应用程序产生:

<?xml version="1.0" encoding="UTF-8"?>
<root>
...
</root>

但是我需要:

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="namespaceURL" 
xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xs:schemaLocation="namespaceURL pathToMySchema.xsd">
...
</root>

我的代码:

StreamResult result = new StreamResult(writer);
Document doc = getDocument();

Transformer trans = transfac.newTransformer();
trans.setOutputProperty(OutputKeys.INDENT, "yes");
trans.setOutputProperty(OutputKeys.METHOD, "xml");
trans.setOutputProperty(OutputKeys.VERSION, "1.0");
trans.setOutputProperty(OutputKeys.ENCODING, "UTF-8");

DOMSource source = new DOMSource(depl.getAsElement(doc));
trans.transform(source, result);

谢谢你的时间,
卡斯滕

4

3 回答 3

6

创建文档时,您可以在根目录中添加命名空间。

String NS_URL = "namespaceURL";

doc = builder.newDocument();
Element root = doc.createElementNS(NS_URL, "root");
root.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance", 
    "xs:schemaLocation", NS_URL + " pathToMySchema.xsd");
doc.appendChild(root);

然后对于添加到文档而不是 createElement() 的每个元素,使用 createElementNS()

doc.createElementNS(NS_URL, name);

这会导致您正在寻找的东西。

<root 
    xmlns="namespaceURL"
    xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" 
    xs:schemaLocation="namespaceURL pathToMySchema.xsd"
    >
于 2012-07-31T09:37:25.073 回答
6

在 XML 数据模型中,命名空间节点实际上不是从父元素读取的,但每个元素都有自己的命名空间节点。因此,简单地向根元素添加一个新的默认命名空间是行不通的,但会产生这样的文档

<root xmlns="namespaceURL">
    <child xmlns=""/>
    ...
</root>

xmlns=""注意子元素上出现空的默认命名空间。实际需要做的是修改每个节点的命名空间或创建一个具有所需默认命名空间的新文档,并将旧文档的内容、元素和属性名称等复制到新文档中。这些可以通过递归地遍历原始文档来完成。我听说,使用 Java DOM 实现这可能会很费力。一种捷径可能是使用不知道命名空间的 DOM 读取文档,然后将新的默认命名空间作为属性添加。其他解决方案是使用 XSLT 转换更改命名空间,这在这种情况下似乎非常合适,因为您实际上已经通过 XSLT 转换生成输出。

使用此 XSLT 样式表将新的默认名称空间和模式位置添加到根元素。此样式表保留旧的命名空间,但将所有元素添加到新的默认命名空间(如果它们以前位于非命名空间中)。

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">

    <!-- Template to add a default namespace to a document -->
    <!-- Elements without a namespace are "moved" to default namespace -->
    <!-- Elements with a namespace are copied as such -->

  <!-- string for default namespace uri and schema location -->
  <xsl:variable name="ns" select="'namespaceURL'"/>
  <xsl:variable name="schemaLoc" select="'namespaceURL pathToMySchema.xsd'"/>

    <!-- template for root element -->
    <!-- adds default namespace and schema location -->
  <xsl:template match="/*" priority="1">
    <xsl:element name="{local-name()}" namespace="{$ns}">
      <xsl:attribute name="xsi:schemaLocation"
        namespace="http://www.w3.org/2001/XMLSchema-instance">
        <xsl:value-of select="$schemaLoc"/>
        </xsl:attribute>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:element>
  </xsl:template>

    <!--template for elements without a namespace -->
  <xsl:template match="*[namespace-uri() = '']">
    <xsl:element name="{local-name()}" namespace="{$ns}">
      <xsl:apply-templates select="@* | node()"/>
    </xsl:element>
  </xsl:template>

    <!--template for elements with a namespace -->
  <xsl:template match="*[not(namespace-uri() = '')]">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

    <!--template to copy attributes, text, PIs and comments -->
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

而不是创建变压器

Transformer trans = transfac.newTransformer();

(它创建一个执行身份转换的样式表),创建一个 XSLT 输入源并将其作为参数提供给newTransformer()

javax.xml.transform.Source xsltSource = new javax.xml.transform.stream.StreamSource(xsltFile);
Transformer trans = transFact.newTransformer(xsltSource);

指向该 XSLT 文件xsltFile的对象在哪里。File

根据需要设置输出属性并transform()在示例代码中调用。结果应该是你想要的,但我没有在 Java 中测试过。给定的 XSLT 文件针对一些琐碎的情况进行了测试,并且在此答案的末尾有一个示例输入和输出。

一些小注释:

  1. 在此过程中不修改原始文档对象。新的默认命名空间仅出现在transform()方法的输出中。
  2. 模式实例名称空间的名称空间前缀通常是xsi:,而不是xs:您的示例代码中(xs:用于模式定义(以及xsd:))。

上面显示的 XSLT 样式表的示例输入和输出

输入:

<root>
    <child>text</child>
    <child attribute="attr-value"/>
    <?pi-target pi-content?>
    <nsx:ns-child xmlns:nsx="ns1x">
        <no-ns-child>text</no-ns-child>
        <!-- comment -->
        <nsx:ns-child nsx:ns-attribute="nsx-attr-value">text</nsx:ns-child>
    </nsx:ns-child>
    <defns-child xmlns="default-ns">
        <def-child attr="val">text</def-child>
        <child xmlns=""/>
    </defns-child>
    <child>text</child>
</root>

输出:

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="namespaceURL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="namespaceURL pathToMySchema.xsd">
    <child>text</child>
    <child attribute="attr-value"/>
    <?pi-target pi-content?>
    <nsx:ns-child xmlns:nsx="ns1x">
        <no-ns-child>text</no-ns-child>
        <!-- comment -->
        <nsx:ns-child nsx:ns-attribute="nsx-attr-value">text</nsx:ns-child>
    </nsx:ns-child>
    <defns-child xmlns="default-ns">
        <def-child attr="val">text</def-child>
        <child xmlns="namespaceURL"/>
    </defns-child>
    <child>text</child>
</root>
于 2011-02-09T23:42:39.757 回答
1

以下是如何向解析器提供提示以解决您的问题: http ://bytes.com/topic/java/answers/16892-xerces-how-perfrom-schema-validations-without-using-xsi-schemalocation

它是这样的:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
dbf.setValidating(true);
dbf.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage",
"http://www.w3.org/2001/XMLSchema");
dbf.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLocation",
"http://www.example.com/Report.xsd");

这是一个带有一些源代码的验证示例。它可能会帮助你。 http://www.ibm.com/developerworks/xml/library/x-tipvalschm/

(如果一切变得更糟,您可以随时搜索和替换。我知道这不是理想的解决方案,但 javax.xml.transform.OutputKeys 似乎没有与 schemalocation 属性相关的成员。)

于 2011-02-08T14:21:25.507 回答