xslt 1.0 中的拆分功能

Posted

技术标签:

【中文标题】xslt 1.0 中的拆分功能【英文标题】:split function in xslt 1.0 【发布时间】:2011-11-17 12:17:50 【问题描述】:

如何在 XSLT 1.0 中拆分节点值?

<mark>1,2</mark>

我需要在for循环中对split输出的每个值执行一些操作。

<xsl:for-each select=""> </xsl:for-each>

如何做到这一点?

【问题讨论】:

值中总是有两项,还是变量数? 它可能会有所不同。它是节点标记的值。 好问题,+1。根据使用的 XSLT 版本(1.0 或 2.0),可以使用递归处理或仅使用标准 XPath 2.0 函数tokenize() 它的 1.0.所以无法使用tokenize。 【参考方案1】:

我。 XSLT 1.0 解决方案

这是在 XSLT 1.0 中仅使用 xxx:node-set() 扩展函数的一种方法

<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:template match="mark">
  <xsl:variable name="vrtfSplit">
   <xsl:apply-templates/>
  </xsl:variable>

  <xsl:for-each select="ext:node-set($vrtfSplit)/*">
   <processedItem>
    <xsl:value-of select="10 * ."/>
   </processedItem>
  </xsl:for-each>
 </xsl:template>

 <xsl:template match="text()" name="split">
  <xsl:param name="pText" select="."/>
   <xsl:if test="string-length($pText) >0">
    <item>
     <xsl:value-of select=
      "substring-before(concat($pText, ','), ',')"/>
    </item>

    <xsl:call-template name="split">
     <xsl:with-param name="pText" select=
     "substring-after($pText, ',')"/>
    </xsl:call-template>
   </xsl:if>
 </xsl:template>
</xsl:stylesheet>

当此转换应用于以下 XML 文档时

<mark>1,2,3,4,5</mark>

产生所需的正确输出(每个项目乘以 10)

<processedItem>10</processedItem>
<processedItem>20</processedItem>
<processedItem>30</processedItem>
<processedItem>40</processedItem>
<processedItem>50</processedItem>

二。 XSLT 2.0 解决方案

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="xs">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="mark">
  <xsl:for-each select="tokenize(., ',')">
   <processedItem>
    <xsl:sequence select="10*xs:integer(.)"/>
   </processedItem>
  </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

【讨论】:

【参考方案2】:

Dimitre Novatchev 的解释很棒,但我们也可以不使用node-set() 函数以更简单的方式做到这一点:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">


    <xsl:output omit-xml-declaration="yes" indent="yes"/>



    <xsl:variable name="delimiter">
        <xsl:text>,</xsl:text>
    </xsl:variable>



    <xsl:template match="mark">
        <xsl:variable name="dataList">
            <xsl:value-of select="."/>
        </xsl:variable>
        <xsl:call-template name="processingTemplate">
            <xsl:with-param name="datalist" select="$dataList"/>
        </xsl:call-template>
    </xsl:template>

    <xsl:template name="processingTemplate">
        <xsl:param name="datalist"/>


        <xsl:choose>
        <xsl:when test="contains($datalist,$delimiter)  ">
                <xsl:element name="processedItem">
                    <xsl:value-of select="substring-before($datalist,$delimiter) * 10"/>
                </xsl:element>
                <xsl:call-template name="processingTemplate">
                    <xsl:with-param name="datalist" select="substring-after($datalist,$delimiter)"/>
                </xsl:call-template>
        </xsl:when>
            <xsl:when test="string-length($datalist)=1">
                <xsl:element name="processedItem">
                    <xsl:value-of select="$datalist * 10"/>

                    </xsl:element>
            </xsl:when>
        </xsl:choose>    

    </xsl:template>
</xsl:stylesheet>

【讨论】:

【参考方案3】:

在 1.0 中,您需要编写递归模板 - 除非您不需要,因为它已经编写好了。从http://www.exslt.org 下载str:tokenize 模板。

【讨论】:

【参考方案4】:

如果您可以使用 exslt,则有一个 tokenize() 函数可以很好地做到这一点。

node-set str:tokenize(string, string?)

见http://www.exslt.org/str/functions/tokenize/

【讨论】:

【参考方案5】:

此代码将在 XSLT 1.0 中拆分分隔字符串 (它适用于 2.0,但不要使用节点集。) 它还可以选择性地抑制字符串中的空元素 或可选的大写元素。

<!-- Example delimited string. -->
<xsl:variable name="delimitedString" select="'a, b, c, ,   , d, e, f, g'"/>

<!-- Create a node set where each node contains one of the elements from the
     delimited string. -->
<xsl:variable name="splitNodes">
  <xsl:call-template name="getNodeListFromDelimitedList">
    <xsl:with-param name="inStrList" select="$delimitedString"/>
    <xsl:with-param name="delimiter" select="','"/>
    <xsl:with-param name="suppressEmptyElements" select="false()"/>
    <xsl:with-param name="upperCase" select="false()"/>
    <xsl:with-param name="allTrim" select="false()"/>
  </xsl:call-template>    
</xsl:variable>

<!-- Use this for XSLT 1.0 only. -->
<xsl:variable name="splitNodesList" select="msxml:node-set($splitNodes)"/>

<!-- Use the split node list to do something.  For example, create a string like 
     the delimited string, but without the delimiters. -->
<xsl:variable name="nonDelimitedString">
  <xsl:for-each select="$splitNodesList/element">
    <xsl:value-of select="."/>
  </xsl:for-each>
</xsl:variable>


<!-- Do something with the nonDelimitedString. -->

<!-- 
*****************************************************************************************

This template converts a delimited string list to a node list as follows:

Each value in the delimited input string is extracted from the string.  Then, a node is 
created to contain the value.   The name of the node is 'element', and it is added to the 
list.  To use this template, create an variable and call this template from within the variable.
If you are using XSLT version 1.0, convert the node list to a node set using the node-set 
function.  You can access the element as follows:  $SomeVariableNodeSet/element

*****************************************************************************************
-->
<xsl:template name="getNodeListFromDelimitedList">
  <!-- Delimited string with one or more delimiters.  -->
  <xsl:param name="inStrList"/>
  <!-- The delimiter. -->
  <xsl:param name="delimiter" select="'|'"/>
  <!-- Set to true to suppress empty elements from being added to node list. Otherwise, set to 'false'.-->
  <xsl:param name="suppressEmptyElements" select="true()"/>
  <!-- Set to true to upper case the strings added to the node list.  -->
  <xsl:param name="upperCase" select="false()"/>
  <!-- Set to true to left trim and right trim the strings added to the nodes list.  -->
  <xsl:param name="allTrim" select="false()"/>

  <xsl:variable name="element">
    <xsl:choose>
      <xsl:when test="contains($inStrList,$delimiter)">
        <xsl:value-of select="substring-before($inStrList,$delimiter)"/> 
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$inStrList"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <!-- Write out the element based on parameters. -->
  <xsl:if test="not($suppressEmptyElements) or normalize-space($element) != ''">
    <!-- Put the element in the list.  -->
    <xsl:element name="element">
      <xsl:choose>
        <xsl:when test="$allTrim">
          <xsl:call-template name="all-trim">
            <xsl:with-param name="inStr" select="$element"/>
            <xsl:with-param name="upperCase" select="$upperCase"/>
          </xsl:call-template>
        </xsl:when>
        <xsl:when test="$upperCase">
          <xsl:value-of select="translate($element, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$element"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:element>
  </xsl:if>

  <xsl:if test="contains($inStrList,$delimiter)">
    <!-- Call template recursively to process the next element. -->
    <xsl:call-template name="getNodeListFromDelimitedList">
      <xsl:with-param name="inStrList" select="substring-after($inStrList,$delimiter)"/>
      <xsl:with-param name="delimiter" select="$delimiter"/>
      <xsl:with-param name="suppressEmptyElements" select="$suppressEmptyElements"/>
      <xsl:with-param name="upperCase" select="$upperCase"/>
      <xsl:with-param name="allTrim" select="$allTrim"/>
    </xsl:call-template>
  </xsl:if>

</xsl:template>


<!-- 
*****************************************************************************************
This template trims the blanks from the left and right sides of a string. 
*****************************************************************************************
-->
<xsl:template name="all-trim">
  <!-- The string that you want to all trim. -->
  <xsl:param name="inStr"/>
  <xsl:param name="upperCase" select="false()"/>

  <xsl:variable name="leftTrimmed">
    <xsl:call-template name="left-trim">
      <xsl:with-param name="inStr" select="$inStr"/>
    </xsl:call-template>
  </xsl:variable>

  <xsl:variable name="rightTrimmed">
    <xsl:call-template name="right-trim">
      <xsl:with-param name="inStr" select="$leftTrimmed"/>
    </xsl:call-template>
  </xsl:variable>

  <xsl:choose>
    <xsl:when test="$upperCase">
      <xsl:value-of select="translate($rightTrimmed, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$rightTrimmed"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<!-- 
*****************************************************************************************
This template trims the blanks from the left side of a string. 
*****************************************************************************************
-->
<xsl:template name="left-trim">
  <!-- The string you want to left trim.  -->
  <xsl:param name ="inStr"/>

  <xsl:choose>
    <xsl:when test="$inStr!=''">
      <xsl:variable name="temp" select="substring($inStr, 1, 1)"/>
      <xsl:choose>
        <xsl:when test="$temp=' '">
          <xsl:choose>
            <xsl:when test="string-length($inStr) &gt; 1">
              <xsl:call-template name="left-trim">
                <xsl:with-param name="inStr" select="substring($inStr, 2, string-length($inStr)-1)"/>
              </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="''"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$inStr"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="''"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>


<!--
*****************************************************************************************
This template trims the blanks from the right side of a string. 
*****************************************************************************************
-->
<xsl:template name="right-trim">
  <!-- The string you want to right trim.  -->
  <xsl:param name ="inStr"/>

  <xsl:choose>
    <xsl:when test="$inStr!=''">
      <xsl:variable name="temp" select="substring($inStr, string-length($inStr), 1)"/>
      <xsl:choose>
        <xsl:when test="$temp=' '">
          <xsl:choose>
            <xsl:when test="string-length($inStr) &gt; 1">
              <xsl:call-template name="right-trim">
                <xsl:with-param name="inStr" select="substring($inStr, 1, string-length($inStr)-1)"/>
              </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="''"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$inStr"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="''"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

【讨论】:

【参考方案6】:

基于@Abhinav 解决方案,我只是简化了递归解决方案以处理通用字符串。我需要拆分的输入字符串是“GEN_EME2_G9_3311|A55;GEN_EME2_G9_3312|A55;foooo_3312|A42”

    <xsl:variable name="delimiter">
       <xsl:text>;</xsl:text>
    </xsl:variable>

    <xsl:template name="fooTemplate">
   ...
        <xsl:choose>        
            <xsl:when test="$conditionlink != ''">      
                <xsl:call-template name="processconditionlinktemplate">
                    <xsl:with-param name="datalist" select="$conditionlink"/>
                </xsl:call-template>            
            </xsl:when>
        </xsl:choose>
    ...
    </xsl:template>

    <xsl:template name="processconditionlinktemplate">
        <xsl:param name="datalist"/>
        <xsl:choose>
            <xsl:when test="contains($datalist,$delimiter)">
                <xsl:element name="processedItem">
                    <xsl:value-of select="substring-before($datalist,$delimiter)"/>
                </xsl:element>
                <xsl:call-template name="processconditionlinktemplate">
                    <xsl:with-param name="datalist" select="substring-after($datalist,$delimiter)"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>     
                <xsl:element name="processedItem">
                    <xsl:value-of select="$datalist"/>  
                </xsl:element>
            </xsl:otherwise>
        </xsl:choose>    
    </xsl:template>

【讨论】:

以上是关于xslt 1.0 中的拆分功能的主要内容,如果未能解决你的问题,请参考以下文章

xslt 有 split() 功能吗?

如何实现 XSLT 标记化功能?

使用父元素在 XSLT 中拆分/分组

使用 xslt 将 xml 复杂节点元素拆分为多个节点

如何在 xslt-1.0 中获取绝对值

XSLT函数以及如何访问函数中的逻辑