XSLT标识转换产生错误的命名空间

时间:2011-05-08 19:29:43

标签: xslt namespaces

我有一个不断发展的自定义XML架构:添加了元素,删除了其他元素,并更改了命名空间以反映新版本(例如,从“http://foo/1.0”到“http:// foo / 1.1" )。我想编写一个XSLT,将XML文档从旧模式转换为新模式。我的第一次尝试有效,但它的冗长和不可扩展,我需要帮助改进它。

以下是1.0架构的示例文档:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<foo:rootElement xmlns:foo="http://foo/1.0">
    <obsolete>something</obsolete>
    <stuff>
        <alpha>a</alpha>
        <beta>b</beta>
    </stuff>
</foo:rootElement>

在1.1模式中,“过时”元素消失但其他一切仍然存在。所以XSLT需要做两件事:

  1. 删除标记
  2. 将命名空间从http://foo/1.0更改为http://foo/1.1
  3. 这是我的解决方案:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="2.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:foo1="http://foo/1.0"
        xmlns:foo="http://foo/1.1" 
        exclude-result-prefixes="foo1">
    
        <xsl:output method="xml" standalone="yes" indent="yes"/>
    
        <!-- Remove the "obsolete" tag -->    
        <xsl:template match="foo1:rootElement/obsolete"/>
    
        <!-- Copy the "alpha" tag -->
        <xsl:template match="foo1:rootElement/stuff/alpha">
            <alpha>
                <xsl:apply-templates/>
            </alpha>
        </xsl:template>
    
        <!-- Copy the "beta" tag -->
        <xsl:template match="foo1:rootElement/stuff/beta">
            <beta>
                <xsl:apply-templates/>
            </beta>
        </xsl:template>
    
        <!-- Copy the "stuff" tag -->
        <xsl:template match="foo1:rootElement/stuff">
            <stuff>
                <xsl:apply-templates/>
            </stuff>
        </xsl:template>
    
        <!-- Copy the "rootElement" tag -->
        <xsl:template match="foo1:rootElement">
            <foo:rootElement>
                <xsl:apply-templates/>
            </foo:rootElement>
        </xsl:template>
    
    </xsl:stylesheet>
    

    虽然这会产生我想要的输出,但请注意我有一个模式中每个元素的复制模板:alpha,beta等。对于包含数百种元素的复杂模式,我必须添加数百个模板!

    我认为我可以通过将所有复制模板减少为单个身份转换来消除此问题,如下所示:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="2.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:foo1="http://foo/1.0"
        xmlns:foo="http://foo/1.1" 
        exclude-result-prefixes="foo1">
    
        <xsl:output method="xml" standalone="yes" indent="yes"/>
    
        <xsl:template match="foo1:rootElement/obsolete"/>
    
        <xsl:template match="node()|@*" name="identity">
            <xsl:copy>
                <xsl:apply-templates select="node()|@*"/>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="foo1:rootElement/stuff">
            <xsl:copy>
                <xsl:call-template name="identity"/>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="foo1:rootElement">
            <foo:rootElement>
                <xsl:apply-templates/>
            </foo:rootElement>
        </xsl:template>
    
    </xsl:stylesheet>
    

    但它产生了:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <foo:rootElement xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:foo="http://foo/1.1">  
        <stuff xmlns:foo="http://foo/1.0">
          <stuff>
            <alpha>a</alpha>
            <beta>b</beta>
          </stuff>
       </stuff>
    </foo:rootElement>
    

    复制了“stuff”元素,这就是我想要的,但它包含在另一个“stuff”元素中,用旧的命名空间标记。我不明白为什么会这样。我已经尝试了很多方法来解决这个问题,但都没有成功。有什么建议?感谢。

2 个答案:

答案 0 :(得分:2)

虽然您可能希望复制属性,但需要在新命名空间中重新生成元素。所以我建议这样做:

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

    <xsl:template match="foo1:rootElement/obsolete"/>

    <xsl:template match="attribute()">
      <xsl:copy/>
    </xsl:template>

    <xsl:template match="element()">
      <xsl:variable name="name" select="local-name()"/>
      <xsl:element name="{$name}" namespace="http://foo/1.1">
        <xsl:apply-templates select="node()|@*"/>
      </xsl:element>
    </xsl:template>

</xsl:stylesheet>

答案 1 :(得分:2)

此转化

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:param name="pOldNS" select="'http://foo/1.0'"/>
 <xsl:param name="pNewNS" select="'http://foo/1.1'"/>

 <xsl:template match="/*">
  <xsl:element name="{name()}" namespace="{$pNewNS}">
   <xsl:apply-templates select="node()|@*"/>
  </xsl:element>
 </xsl:template>

 <xsl:template match="*">
  <xsl:element name="{name()}">
   <xsl:copy-of select="namespace::*[not(.=$pOldNS)]"/>

   <xsl:apply-templates select="node()|@*"/>
  </xsl:element>
 </xsl:template>

 <xsl:template match="@*">
  <xsl:choose>
   <xsl:when test="namespace-uri()=$pOldNS">
    <xsl:attribute name="{name()}" namespace="{$pNewNS}">
     <xsl:value-of select="."/>
    </xsl:attribute>
   </xsl:when>
   <xsl:otherwise>
    <xsl:copy-of select="."/>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>

 <xsl:template match="obsolete"/>
</xsl:stylesheet>

应用于此XML文档时

<foo:rootElement xmlns:foo="http://foo/1.0">
    <obsolete>something</obsolete>
    <stuff>
        <alpha x="y" foo:t="z">a</alpha>
        <beta>b</beta>
    </stuff>
</foo:rootElement>

生成所需的正确结果(删除obsolute个元素,升级名称空间,正确处理属性节点,包括旧命名空间中的节点):

<foo:rootElement xmlns:foo="http://foo/1.1">
   <stuff>
      <alpha x="y" foo:t="z">a</alpha>
      <beta>b</beta>
   </stuff>
</foo:rootElement>

此解决方案的主要优势

  1. 当存在属于旧架构命名空间的属性时正常工作

  2. 常规,允许旧的和新的命名空间作为外部参数传递给转换。