zoukankan      html  css  js  c++  java
  • 在XSLT中对foreach语句使用distinct操作

      XSLT用来解析XML文档并按照规定的样式输出数据。在XSLT中,我们通常使用for-each元素来遍历XML中的循环节点并输出内容,for-each元素允许你对要遍历的节点进行排序,参考文章“xslt中的for-each排序”。可是,如何在使用for-each元素时对要遍历的节点进行distinct操作以消除重复节点呢?先看下面的XML片段:

    <addresses>
      <address>
        <state>FL</state>
      </address>
      <address>
        <state>GA</state>
      </address>
      <address>
        <state>MN</state>
      </address>
      <address>
        <state>FL</state>
      </address>
    </addresses>

      如何编写XSLT代码让其输出为下面的内容?

    <states>
      <state>FL</state>
      <state>GA</state>
      <state>MN</state>
    </states>

      注意,上面的XML片段中,节点<address/>为重复节点,并且子节点<state/>存在重复的值,在输出的内容中将去掉这些具有重复节点。我们可以定义一个Key元素: 

    <xsl:key name=”distinctState” match=”addresses/address” use=”./state”></xsl:key>

       Key元素必须定义在元素xsl:template的外面,与元素xsl:template平级。在上面的key元素中,我们将key应用到addresses/address节点上,并规定该key的表达式为节点state的值。函数generate-id()用于返回唯一标识指定节点的字符串值。然后,我们在for-each元素中这样使用:

    <xsl:key name="distinctState" match="addresses/address" use="./state"></xsl:key>

    <xsl:template match="/">
      <states>
          <xsl:for-each select="addresses/address[generate-id() = generate-id(key('distinctState', ./state))]">
            <state>
              <xsl:value-of select="./state"></xsl:value-of>
            </state>
          </xsl:for-each>
      </states>
    </xsl:template>

       key元素的表达式中也可以使用函数来进行更加精确的匹配,如:

    <xsl:key name="distinctState" match="/Customers/Customer" use="substring(Address, string-length(Address)-1)"></xsl:key>

    <xsl:template match="/">
      <xsl:for-each select="Customers/Customer[generate-id() = generate-id(key('distinctState', substring(Address, (string-length(Address)-1))))]">
        <xsl:call-template name="AggregateForState">
          <xsl:with-param name="state" select="substring(Address, (string-length(Address)-1))"/>
        </xsl:call-template>
      </xsl:for-each>
    </xsl:template>

      在看一个复杂点的例子,对XML元素进行分组输出: 

    <items>
      <item>
        <name>name1</name>
        <group>group1</group>
      </item>
      <item>
        <name>name2</name>
        <group>group1</group>
      </item>
      <item>
        <name>name3</name>
        <group>group2</group>
      </item>
      <item>
        <name>name4</name>
        <group>group2</group>
      </item>
      <item>
        <name>name5</name>
        <group>group2</group>
      </item>
      <item>
        <name>name6</name>
        <group>group1</group>
      </item>
      <item>
        <name>name7</name>
        <group>group3</group>
      </item>
      <item>
        <name>name8</name>
        <group>group3</group>
      </item>
      <item>
        <name>name9</name>
        <group>group4</group>
      </item>
      <item>
        <name>name10</name>
        <group>group1</group>
      </item>
    </items>

      我们希望编写XSLT将上面的XML解析成下面的样子:

      完整的XSLT代码如下:

    <?xml version="1.0" encoding="utf-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:msxsl
    ="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
    >
      <xsl:output method="html" indent="yes" omit-xml-declaration="yes"/>

      <xsl:key name="distinctState" match="items/item" use="./group"/>

      <xsl:template match="/">
        <xsl:variable name="tabStr">
          <xsl:for-each select="items/item[generate-id()=generate-id(key('distinctState', ./group))]">
            <xsl:value-of select="./group"/>
            <xsl:if test="position()!=last()">|</xsl:if>
          </xsl:for-each>
        </xsl:variable>

        tabStr: <xsl:value-of select="$tabStr"/>
        <br/>
        <br/>
        <xsl:for-each select="items/item[generate-id()=generate-id(key('distinctState', ./group))]">
          <xsl:variable name="tabName">
            <xsl:call-template name="output-tokens">
              <xsl:with-param name="list" select="$tabStr" />
              <xsl:with-param name="separator">|</xsl:with-param>
              <xsl:with-param name="pos" select="position()"/>
            </xsl:call-template>
          </xsl:variable>
          <xsl:value-of select="$tabName"/>
          <br/>
          <hr/>
          <xsl:for-each select="//items/item">
            <xsl:if test="./group = $tabName">
              <xsl:value-of select="name"/>
              <br/>
            </xsl:if>
          </xsl:for-each>
          <br/>
        </xsl:for-each>
      </xsl:template>

      <xsl:template name="output-tokens">
        <xsl:param name="list" />
        <xsl:param name="separator" />
        <xsl:param name="pos" />
        <xsl:variable name="newlist" select="concat(normalize-space($list), $separator)" />
        <xsl:variable name="first" select="substring-before($newlist, $separator)" />
        <xsl:variable name="remaining" select="substring-after($newlist, $separator)" />

        <xsl:choose>
          <xsl:when test="$pos = 1">
            <xsl:value-of select="$first"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:call-template name="output-tokens">
              <xsl:with-param name="list" select="$remaining" />
              <xsl:with-param name="separator" select="$separator" />
              <xsl:with-param name="pos" select="$pos - 1"/>
            </xsl:call-template>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:template>

    </xsl:stylesheet>

      在上面的代码中,我们首先定义了元素key,并应用到节点items/item上,表达式为“./group”。变量tabStr用来存放以字符“|”分隔的group节点的值,并使用了distinct操作。接下来我们在页面上打印了变量tabStr的值。紧接着的for-each元素则将整个XML文档按照group分组进行输出。注意自定义的template “output-tokens”,在其中使用了一点技巧用来按不同的分组找出对应的分组名称,类似于C#中将字符串使用Split函数存放到数组中。有关output-tokens模板的技巧可以参考我的另一篇文章“在xslt中实现split方法对查询字符串进行分隔”。

      在XSLT的使用中有许多的技巧,灵活掌握这些技巧可以大大缩短我们的开发时间。

  • 相关阅读:
    (转)WCF中的REST是什么
    DrpList
    IIS代码管理(1):遍历应用程序池和属性
    rdp,ListBox,Drp
    在.NET中杀死Word,Excel等进程
    IIS代码管理(2):创建应用程序池和属性
    防止用户重复登录
    asp.net2.0的几种自动生成脚本的原理以及应用
    工厂模式new问题
    我们需要什么样的字段类型?
  • 原文地址:https://www.cnblogs.com/jaxu/p/2265868.html
Copyright © 2011-2022 走看看