使用参数匹配inx XSLT中的元素

时间:2012-06-26 14:57:31

标签: xml xslt

我从另一篇文章中获得了以下模板..

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

<xsl:param name="pUncertainElName" select="'second'"/>
<xsl:param name="pParentPath" select="'outerElement/innerElement'" />
<xsl:param name="pOrderedNames" select="'|first|second|third|'"/>

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

<xsl:template match="outerElement/innerElement">
  <xsl:variable name="vrtfFirstPass">
      <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
      <xsl:apply-templates select=
        "self::*[not(*[name() = $pUncertainElName])
                or
                *[name()=$pUncertainElName and @missing-cause]]"
        mode="missing"/>
      </xsl:copy>
  </xsl:variable>

  <xsl:apply-templates select="ext:node-set($vrtfFirstPass)/*" mode="pass2"/>
</xsl:template>

<xsl:template match="*[@missing-cause]"/>

<xsl:template match="*" mode="missing">
    <xsl:element name="{$pUncertainElName}">
        <CharacterString>INSERTED BY TEMPLATE</CharacterString>
    </xsl:element>
</xsl:template>

<xsl:template match="outerElement/innerElement" mode="pass2">
  <xsl:copy>
  <xsl:apply-templates>
    <xsl:sort data-type="number" select=
    "string-length(substring-before($pOrderedNames,
                                    concat('|', name(), '|')
                                    )
                  )"/>
  </xsl:apply-templates>
  </xsl:copy>
</xsl:template>
</xsl:stylesheet>

目的是添加缺少的元素,添加源文档中缺少的特定位置。 sourcedocument看起来像这样

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<doc>
  <outerElement>
    <innerElement>
      <first>
        <textElement>Some Text</textElement>
      </first>
      <second missing-cause="bla" />
      <third>
        <textElement>Some Text</textElement>
      </third>
    </innerElement>
  </outerElement>
</doc>

我必须以类似的方式添加许多这样的元素,所以我想使用参数来指定父路径和我想要插入的元素。

所以这是第一个问题:如何在匹配中使用参数? match =“$ parameter”或变体似乎不起作用。

第二个: 使用此模板添加元素仍然存在问题,我认为该模板来自第二遍。

如果我的文档看起来像上面发布的那样,则会将输出展平为

<doc>
  <outerElement>Some TextSome TextINSERTED BY TEMPLATE</outerElement>
</doc>

如果错过了它应该工作的话。在第二条道路的建设中最有可能遗漏了一些东西,但我无法弄清楚如何解决这个问题。

最后一个..是否可以在单个文档上使用不同参数调用此模板20次以进行转换,或者我应该尝试其他方法吗?

再次感谢您的帮助,抱歉我是新手;)

1 个答案:

答案 0 :(得分:1)

在XSLT 1.0和XSLT 2.0中,无法动态评估XPath表达式。

因此,您尝试对$pParentPath执行的操作将无法产生所需的结果。

作为一种解决方法,您可以传递两个不同的参数:pchildNamepgrandchildName并使用以下内容:

*[name()=$pchildName]/*[name()=$pgrandchildName]

在XSLT 1.0中,匹配模式中禁止使用变量或参数引用。在XSLT 2.0中,没关系。

以下是转换,已针对此特定XML文档进行了更正:

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

    <xsl:param name="pUncertainElName" select="'second'"/>
    <xsl:param name="pOrderedNames" select="'|first|second|third|'"/>

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

    <xsl:template match="innerElement">
      <xsl:variable name="vrtfFirstPass">
          <xsl:copy>
          <xsl:apply-templates select="node()|@*"/>
          <xsl:apply-templates select=
            "self::*[not(*[name() = $pUncertainElName])
                    or
                    *[name()=$pUncertainElName and @missing-cause]]"
            mode="missing"/>
          </xsl:copy>
      </xsl:variable>

      <xsl:apply-templates select="ext:node-set($vrtfFirstPass)/*" mode="pass2"/>
    </xsl:template>

    <xsl:template match="*[@missing-cause]"/>

    <xsl:template match="*" mode="missing">
        <xsl:element name="{$pUncertainElName}">
            <CharacterString>INSERTED BY TEMPLATE</CharacterString>
        </xsl:element>
    </xsl:template>

    <xsl:template match="innerElement" mode="pass2">
      <xsl:copy>
      <xsl:apply-templates>
        <xsl:sort data-type="number" select=
        "string-length(substring-before($pOrderedNames,
                                        concat('|', name(), '|')
                                        )
                      )"/>
      </xsl:apply-templates>
      </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

在提供的XML文档上应用此转换时:

<doc>
    <outerElement>
        <innerElement>
            <first>
                <textElement>Some Text</textElement>
            </first>
            <second missing-cause="bla" />
            <third>
                <textElement>Some Text</textElement>
            </third>
        </innerElement>
    </outerElement>
</doc>

产生了想要的正确结果:

<doc>
   <outerElement>
      <innerElement>
         <first>
            <textElement>Some Text</textElement>
         </first>
         <second>
            <CharacterString>INSERTED BY TEMPLATE</CharacterString>
         </second>
         <third>
            <textElement>Some Text</textElement>
         </third>
      </innerElement>
   </outerElement>
</doc>

可以修改转换,以便它处理文档层次结构不同位置的不同元素的子元素

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

    <xsl:param name="pUncertainElName" select="'second'"/>
    <xsl:param name="pOrderedNames" select="'|first|second|third|'"/>

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

    <xsl:template match="/">
      <xsl:variable name="vrtfFirstPass">
        <xsl:apply-templates select="node()|@*"/>
      </xsl:variable>

      <xsl:apply-templates select="ext:node-set($vrtfFirstPass)/*" mode="pass2"/>
    </xsl:template>

    <xsl:template match="innerElement">
      <xsl:copy>
          <xsl:apply-templates select="node()|@*"/>
          <xsl:apply-templates select=
            "self::*[not(*[name() = $pUncertainElName])
                    or
                    *[name()=$pUncertainElName and @missing-cause]]"
            mode="missing"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*[@missing-cause]"/>

    <xsl:template match="*" mode="missing">
        <xsl:element name="{$pUncertainElName}">
            <CharacterString>INSERTED BY TEMPLATE</CharacterString>
        </xsl:element>
    </xsl:template>

    <xsl:template match="node()|@*" mode="pass2">
      <xsl:copy>
      <xsl:apply-templates select="node()|@*" mode="pass2"/>
      </xsl:copy>
    </xsl:template>

    <xsl:template match="innerElement" mode="pass2">
      <xsl:copy>
       <xsl:apply-templates>
         <xsl:sort data-type="number" select=
        "string-length(substring-before($pOrderedNames,
                                        concat('|', name(), '|')
                                        )
                      )"/>
       </xsl:apply-templates>
      </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

此转换应用于以下XML文档(包含两个innerElement元素 - 具有不同父级且位于不同深度 - 其子级需要专门处理):

<doc>
    <outerElement>
        <innerElement>
            <first>
                <textElement>Some Text</textElement>
            </first>
            <second missing-cause="bla" />
            <third>
                <textElement>Some Text</textElement>
            </third>
        </innerElement>
        <outerElement2>
          <outerElement3>
            <innerElement>
                    <first>
                        <textElement>Some Text</textElement>
                    </first>
                    <third>
                        <textElement>Some Text</textElement>
                    </third>
            </innerElement>
          </outerElement3>
        </outerElement2>
    </outerElement>
</doc>

产生了想要的正确结果

<doc>
   <outerElement>
      <innerElement>
         <first>
            <textElement>Some Text</textElement>
         </first>
         <second>
            <CharacterString>INSERTED BY TEMPLATE</CharacterString>
         </second>
         <third>
            <textElement>Some Text</textElement>
         </third>
      </innerElement>
      <outerElement2>
         <outerElement3>
            <innerElement>
               <first>
                  <textElement>Some Text</textElement>
               </first>
               <second>
                  <CharacterString>INSERTED BY TEMPLATE</CharacterString>
               </second>
               <third>
                  <textElement>Some Text</textElement>
               </third>
            </innerElement>
         </outerElement3>
      </outerElement2>
   </outerElement>
</doc>

最后,我们可以进一步修改转换,以便它可以处理不同命名父项的子项 - 比如说innerElementsomeOtherInnerElement

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

    <xsl:param name="pUncertainElName" select="'second'"/>
    <xsl:param name="pOrderedNames" select="'|first|second|third|'"/>

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

    <xsl:template match="/">
      <xsl:variable name="vrtfFirstPass">
        <xsl:apply-templates select="node()|@*"/>
      </xsl:variable>

      <xsl:apply-templates select="ext:node-set($vrtfFirstPass)/*" mode="pass2"/>
    </xsl:template>

    <xsl:template match="innerElement | someOtherInnerElement">
      <xsl:copy>
          <xsl:apply-templates select="node()|@*"/>
          <xsl:apply-templates select=
            "self::*[not(*[name() = $pUncertainElName])
                    or
                    *[name()=$pUncertainElName and @missing-cause]]"
            mode="missing"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*[@missing-cause]"/>

    <xsl:template match="*" mode="missing">
        <xsl:element name="{$pUncertainElName}">
            <CharacterString>INSERTED BY TEMPLATE</CharacterString>
        </xsl:element>
    </xsl:template>

    <xsl:template match="node()|@*" mode="pass2">
      <xsl:copy>
      <xsl:apply-templates select="node()|@*" mode="pass2"/>
      </xsl:copy>
    </xsl:template>

    <xsl:template match="innerElement | someOtherInnerElement" mode="pass2">
      <xsl:copy>
       <xsl:apply-templates>
         <xsl:sort data-type="number" select=
        "string-length(substring-before($pOrderedNames,
                                        concat('|', name(), '|')
                                        )
                      )"/>
       </xsl:apply-templates>
      </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

在以下XML文档中应用此转换时,要以所需方式处理的子项具有以这两个名称(innerElementsomeOtherInnerElement)命名的父项:

<doc>
    <outerElement>
        <innerElement>
            <first>
                <textElement>Some Text</textElement>
            </first>
            <second missing-cause="bla" />
            <third>
                <textElement>Some Text</textElement>
            </third>
        </innerElement>
        <outerElement2>
          <outerElement3>
            <someOtherInnerElement>
                    <first>
                        <textElement>Some Text</textElement>
                    </first>
                    <third>
                        <textElement>Some Text</textElement>
                    </third>
            </someOtherInnerElement>
          </outerElement3>
        </outerElement2>
    </outerElement>
</doc>

再次生成想要的正确结果:

<doc>
   <outerElement>
      <innerElement>
         <first>
            <textElement>Some Text</textElement>
         </first>
         <second>
            <CharacterString>INSERTED BY TEMPLATE</CharacterString>
         </second>
         <third>
            <textElement>Some Text</textElement>
         </third>
      </innerElement>
      <outerElement2>
         <outerElement3>
            <someOtherInnerElement>
               <first>
                  <textElement>Some Text</textElement>
               </first>
               <second>
                  <CharacterString>INSERTED BY TEMPLATE</CharacterString>
               </second>
               <third>
                  <textElement>Some Text</textElement>
               </third>
            </someOtherInnerElement>
         </outerElement3>
      </outerElement2>
   </outerElement>
</doc>

<强>解释

这与前一个问题的逻辑基本相同:

  1. 双程处理。

  2. 覆盖身份规则。

  3. 正确使用模板和模板匹配模式。

  4. 按照名称的首选顺序对元素进行排序。