在基于XSL的转换后删除空的xmlns元素

时间:2013-12-16 15:30:37

标签: xml xslt transformation xml-namespaces

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

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

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

XSLT的SNIPPET

<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文件发送回处理程序程序,然后将<xformResultData>中的所有元素输出到文件中。这就是问题所在。输出文件如下所示:

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

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

2 个答案:

答案 0 :(得分:6)

命名空间声明是不是属性,即使它们看起来相同。如果输出中的元素上出现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元素在命名空间中。如果您将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

答案 1 :(得分: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>

<强> EDIT2:

这是一个允许验证任何内容的许可模式,您只需要根元素的名称和名称空间(对于模式的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>