【问题标题】:Removing empty xmlns element after XSL based transformation在基于 XSL 的转换之后删除空的 xmlns 元素
【发布时间】:2013-12-16 15:30:37
【问题描述】:

最近,我一直在使用一个数据转换工具,它使用 XSL 来修改输入数据的格式。我最近有been having problems with the namespaces,现在我遇到了一个新问题,由上一个问题的解决方案引起。

正确的 xmlns 存储在父元素中,但是第一个子元素(唯一的第一级子节点)包含属性xmlns=""。我发现了一些类似的问题,但问题/实施方法的不同足以阻止我直接应用更改。有谁知道如何阻止该属性应用于子数据?我曾想过走我之前走的路(通过序列化 XML,然后进行字符串操作来修复它),但所需的序列化函数仅存在于 xpath 3 中,而我使用的转换服务器仅支持xpath 2,遗憾的是我没有发言权:(

我正在使用 Map Force 来构建 XSL 转换,因此不能简单地编辑 XSL(因为它将被 map force 覆盖),但我相信我可以将 XSL 更改应用于 Map Force。

XSLT 的片段

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:core="http://www.altova.com/MapForce/UDF/core" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" exclude-result-prefixes="core xs fn">
    <xsl:template name="core:firstCharacter">
        ...
    </xsl:template>
    <xsl:template name="core:tokenize-by-length-internal">
        ...
    </xsl:template>
    <xsl:output method="xml" encoding="UTF-8" byte-order-mark="no" indent="yes"/>
    <xsl:template match="/">
        <xsl:variable name="var1_SwiftMessages" as="node()?" select="SwiftMessages"/>
        <xformResult xmlns="urn:...">
            <xsl:attribute name="xsi:schemaLocation" namespace="http://www.w3.org/2001/XMLSchema-instance" select="'urn:... OutputInterface/xformResult.xsd'"/>
            <xformResultRecord>
                <xformResultData>
                    <Document>
                        <!-- REMAINDER OF FAIRLY STANDARD CODE -->
                    </Document>
                </xformResultData>
            </xformResultRecord>
        </xformResult>
    </xsl:template>

urn:... 是对输出文件规范的引用,xformResult.xsd 是输出文件的 Schema。

然后将转换器 XML 文件发送回处理程序,然后将 &lt;xformResultData&gt; 中的所有元素输出到文件中。这就是问题所在。输出文件如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:..." xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <Cstmr xmlns="">
        <!-- REMAINDER OF GENERATED XML -->
    </Cstmr>
</Document>

如您所见,Document (Cstmr) 的第一级子级已通过转换器输出将xmlns="" 添加到元素中。当我用 Map Force 测试它时,它不包括在内,但它在 xform 工具的输出中。 xform 工具基于 SAXON,对它的调用是相当标准的 XML 函数。

【问题讨论】:

  • 产生空命名空间的 XSLT 是什么样的?
  • 您没有向我们展示生成 Cstmr 元素的代码,因此很难建议您如何更改它。无论代码是什么,它都会在没有命名空间的情况下生成 Cstmr 元素,而应该在 urn:... 命名空间中生成元素。

标签: xml xslt transformation xml-namespaces


【解决方案1】:

命名空间声明是 not 属性,即使它们看起来相同。如果您的输出中的某个元素上出现了xmlns="",则这意味着您在默认命名空间已经生效的位置向树中添加了一个没有命名空间的元素。为了输出这样的结构,序列化程序必须使用xmlns="" 来抵消默认值。

要解决此问题,您需要先在正确的命名空间中创建元素。例如,假设您的输入 XML 类似于

<example xmlns="http://example.com">
  <child1/>
</example>

并且您想将child1 替换为child2。以下:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="@*|node()">
    <xsl:copy><xsl:apply-templates select="@*|node()" /></xsl:copy>
  </xsl:template>

  <!-- replace children of the document element with child2 -->
  <xsl:template match="/*/*">
    <child2 /><!-- child2 is in no namespace -->
  </xsl:template>
</xsl:stylesheet>

会产生与您看到的结果相似的结果

<example xmlns="http://example.com">
  <child2 xmlns=""/>
</example>

因为样式表在没有命名空间中创建 child2 元素。但是如果你将xmlns="http://example.com" 添加到样式表

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
                xmlns="http://example.com">
  <xsl:template match="@*|node()">
    <xsl:copy><xsl:apply-templates select="@*|node()" /></xsl:copy>
  </xsl:template>

  <!-- replace children of the document element with child2 -->
  <xsl:template match="/*/*">
    <child2 /><!-- child2 is now in the http://example.com namespace -->
  </xsl:template>
</xsl:stylesheet>

那么它会产生正确的结果

<example xmlns="http://example.com">
  <child2 />
</example>

如果您不想将默认声明添加到整个样式表,您可以将其本地化在负责创建输出元素的模板上,或者实际上在元素本身上,例如

  <xsl:template match="/*/*" xmlns="http://example.com">
    <child2/>
  </xsl:template>

编辑:参考您的具体示例:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:core="http://www.altova.com/MapForce/UDF/core" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" exclude-result-prefixes="core xs fn">
    <xsl:template name="core:firstCharacter">
        ...
    </xsl:template>
    <xsl:template name="core:tokenize-by-length-internal">
        ...
    </xsl:template>
    <xsl:output method="xml" encoding="UTF-8" byte-order-mark="no" indent="yes"/>
    <xsl:template match="/">
        <xsl:variable name="var1_SwiftMessages" as="node()?" select="SwiftMessages"/>
        <xformResult xmlns="urn:...">
            <xsl:attribute name="xsi:schemaLocation" namespace="http://www.w3.org/2001/XMLSchema-instance" select="'urn:... OutputInterface/xformResult.xsd'"/>
            <xformResultRecord>
                <xformResultData>
                    <Document>
                        <!-- example of code that might be in here -->
                        <xsl:apply-templates select="$var1_SwiftMessages/Customer" />
                    </Document>
                </xformResultData>
            </xformResultRecord>
        </xformResult>
    </xsl:template>

    <xsl:template match="Customer">
        <Cstmr>
            <!-- contents of Cstmr element -->
        </Cstmr>
    </xsl:template>

这里要理解的关键是,应用于文字结果元素的命名空间绑定是那些在被视为纯 XML 文档时在 样式表 中的相关位置有效的绑定。 xformResultxformResultRecordxformResultDataDocument 元素位于 urn:... 命名空间中,因为它在样式表中的 xformResult 元素上被声明为默认值,但在 Customer 中的 Cstmr 元素模板在命名空间中是 not。相反,如果您将xmlns 声明从xformResult 上移到xsl:stylesheet

<xsl:stylesheet version="2.0" xmlns="urn:..." xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:core="http://www.altova.com/MapForce/UDF/core" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" exclude-result-prefixes="core xs fn">
    <xsl:template name="core:firstCharacter">
        ...
    </xsl:template>
    <xsl:template name="core:tokenize-by-length-internal">
        ...
    </xsl:template>
    <xsl:output method="xml" encoding="UTF-8" byte-order-mark="no" indent="yes"/>
    <xsl:template match="/">
        <xsl:variable name="var1_SwiftMessages" as="node()?" select="SwiftMessages"/>
        <xformResult>
            <xsl:attribute name="xsi:schemaLocation" namespace="http://www.w3.org/2001/XMLSchema-instance" select="'urn:... OutputInterface/xformResult.xsd'"/>
            <xformResultRecord>
                <xformResultData>
                    <Document>
                        <!-- example of code that might be in here -->
                        <xsl:apply-templates select="$var1_SwiftMessages/Customer" />
                    </Document>
                </xformResultData>
            </xformResultRecord>
        </xformResult>
    </xsl:template>

    <xsl:template match="Customer">
        <Cstmr>
            <!-- contents of Cstmr element -->
        </Cstmr>
    </xsl:template>

那么这会将所有不带前缀的文字结果元素放入urn:...命名空间,包括Cstmr

【讨论】:

    【解决方案2】:

    您的转换(或输入数据)的其他地方一定有问题。这不是序列化的问题。

    下面的两个例子有不同的含义。在第一个 body 中有 xhtml 命名空间,因为它是默认的,设置在根元素上。

    <html xmlns="http://www.w3.org/1999/xhtml">
    <body>
    </body>
    </html>
    

    在第二个示例中,您覆盖默认命名空间并明确指出body 节点来自命名空间空字符串。与 xhtml 命名空间中的对象不同。

    <html xmlns="http://www.w3.org/1999/xhtml">
    <body xmlns="">
    </body>
    </html>
    

    编辑:

    如果可以选择进行其他转换,则以下 XSLT 会将父级的命名空间(实际上是最接近的合适祖先的命名空间)应用于具有空命名空间的元素:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
        <xsl:template priority="2" match="*">
            <xsl:choose>
                <xsl:when test="namespace-uri()=''">
                    <xsl:element name="{local-name()}" namespace="{namespace-uri(ancestor::*[namespace-uri()!=''][1])}">
                        <xsl:apply-templates select="@* | node()"/>
                    </xsl:element>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:element name="{local-name()}" namespace="{namespace-uri()}">
                        <xsl:apply-templates select="@* | node()"/>
                    </xsl:element>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:template>
    
        <xsl:template priority="1" match="@* | node()">
            <xsl:copy-of select="."/>
        </xsl:template>
    
    </xsl:stylesheet>
    

    编辑2:

    这是一个可以验证任何内容的许可模式,您只需要根元素的名称和命名空间(用于模式的 targetNamespace 属性)。对于演示,我遵循上面的 html 示例。

    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema version="1.0"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://www.w3.org/1999/xhtml"
        elementFormDefault="qualified">
    
      <xs:element name="html">
        <xs:complexType>
          <xs:sequence>
            <xs:any processContents="lax" minOccurs="0" maxOccurs="unbounded" />
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    
    </xs:schema>
    

    【讨论】:

    • 这几乎就是我遇到的问题(但不是 html,而是一种适当的 xml 表单)。 xmlns="" 在您的 body 标签中是我在我的标签上看到的。但是如何阻止 Map Force 强行插入xmlns=""?从我在互联网上找到的内容来看,填充数据的模板似乎不在命名空间中,因此它被插入到默认命名空间("")中,所以我需要找到一个阻止 Map Force 在应该继承命名空间时认为命名空间不同的方法
    • @cgoddard 共享您生成的 XSLT 可能会有所帮助。 (如果不是保密的。)
    • 超级机密(显然银行似乎认为在其社区内共享的信息无法传播到更广阔的世界,以至于要获得规范,您需要签署 200 页的 NDA... )。无论如何,xslt 很大,完整的映射是 57k,超过 2000 行
    • 哦,看起来很漂亮。我可以在该过程中创建另一个步骤(目前,有 4 个,这是最终的转换),所以我可以将它直接作为 XSL 转换(没有 mapforce)放在最后,并使用它来删除任何空的命名空间。谢谢,我会告诉你效果如何
    • 该死,已经给了你一个 +1,如果只有 SE 允许的不仅仅是 +1 和 -1 来判断(我知道有标记作为答案,但这仅适用于它的工作)
    猜你喜欢
    • 2014-03-12
    • 2020-03-24
    • 1970-01-01
    • 1970-01-01
    • 2011-03-31
    • 1970-01-01
    • 2016-05-20
    • 1970-01-01
    • 2017-02-03
    相关资源
    最近更新 更多