首页 > 解决方案 > 在 XSLT 中的 ForEach 下使用自定义函数选择不同的值

问题描述

在使用自定义函数在 ForEach 循环中迭代时,我需要忽略重复值。我有以下示例并分享正在尝试做的 xslt。我不能在 xslt 2.0 中应用 foreach 组,因为它会破坏现有的代码功能。我期待用自定义函数本身解决这个问题。

自定义功能:

<xsl:function name="OriginalBook.Genre">
    <xsl:param name="book"/>
    <xsl:for-each select="$book[price &lt 10]">
       <xsl:element name="OriginalGenre">
          <xsl:if test="book[not(preceding::genre)]">
            <xsl:value-of select="current()/genre"/>
          </xsl:if>
      </xsl:element>
    </xsl:for-each>
</xsl:function>

<xsl:template match="/">
    <OriginalBook>  
        <xsl:copy-of select="OriginalBook.Genre(/catalog/book)"/>
    </OriginalBook>
</xsl:template>

输入:

<?xml version="1.0"?>
<catalog>
   <book>
      <author>Gambardella, Matthew</author>
      <title>XML Developer's Guide</title>
      <genre>Computer</genre>
      <price>9.95</price>
      <publish_date>2000-10-01</publish_date>
   </book>
   <book>
      <author>Ralls, Kim</author>
      <title>XML Developer's Guide</title>
      <genre>Computer</genre>
      <price>5.95</price>
      <publish_date>2000-12-16</publish_date>
   </book>
   <book>
      <author>Corets, Eva</author>
      <title>Maeve Ascendant</title>
      <genre>Fantasy</genre>
      <price>5.95</price>
      <publish_date>2000-11-17</publish_date>
   </book>
   <book>
      <author>Galos, Mike</author>
      <title>Visual Studio 7: A Comprehensive Guide</title>
      <genre>Computer</genre>
      <price>49.95</price>
      <publish_date>2001-04-16</publish_date>
   </book>
</catalog>

期望的输出:

<OriginalBook>
    <OriginalGenre>Computer</OriginalGenre>
    <OriginalGenre>Fantasy</OriginalGenre>
</OriginalBook>

标签: xmlxsltxslt-groupingxslt-3.0

解决方案


对于它的价值,假设 XSLT 3 消除重复的经典方法是for-each-group group-bydistinct-valuesmap:merge

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:map="http://www.w3.org/2005/xpath-functions/map"
    exclude-result-prefixes="#all"
    xmlns:mf="http://example.com/mf"
    expand-text="yes"
    version="3.0">
    
  <xsl:function name="mf:grouping-example" as="element(OriginalGenre)*">
      <xsl:param name="books" as="element(book)*"/>
      <xsl:for-each-group select="$books[price &lt; 10]" group-by="genre">
          <OriginalGenre>{current-grouping-key()}</OriginalGenre>
      </xsl:for-each-group>
  </xsl:function>
  
  <xsl:function name="mf:distinct-values-example" as="element(OriginalGenre)*">
      <xsl:param name="books" as="element(book)*"/>
      <xsl:for-each select="distinct-values($books[price &lt; 10]/genre)">
          <OriginalGenre>{.}</OriginalGenre>
      </xsl:for-each>
  </xsl:function>
  
  <xsl:function name="mf:map-merge-example" as="element(OriginalGenre)*">
      <xsl:param name="books" as="element(book)*"/>
      <xsl:for-each 
          select="$books[price &lt; 10]/genre!map { data() : . } 
                  => map:merge() => map:keys()">
          <OriginalGenre>{.}</OriginalGenre>
      </xsl:for-each>
  </xsl:function>
  
  <xsl:output indent="yes"/>
  
  <xsl:template match="/">
      <Results>
          <Result-Grouping>
              <xsl:sequence select="mf:grouping-example(catalog/book)"/>
          </Result-Grouping>
          <Result-distinct-values>
              <xsl:sequence select="mf:distinct-values-example(catalog/book)"/>
          </Result-distinct-values>
          <Result-map-merge-example>
              <xsl:sequence select="mf:map-merge-example(catalog/book)"/>
          </Result-map-merge-example>
      </Results>
  </xsl:template>
  
</xsl:stylesheet>

顺序循环的“等效”不是xsl:for-each,而是xsl:iterate

  <xsl:function name="mf:iterate-example" as="element(OriginalGenre)*">
      <xsl:param name="books" as="element(book)*"/>
      <xsl:iterate select="$books[price &lt; 10]/genre/data()">
          <xsl:param name="genres" as="xs:string*" select="()"/>
          <xsl:if test="not(. = $genres)">
            <OriginalGenre>{.}</OriginalGenre>
          </xsl:if>
          <xsl:next-iteration>
            <xsl:with-param name="genres" select="if (. = $genres) then $genres else ($genres, .)"/>
          </xsl:next-iteration>
      </xsl:iterate>
  </xsl:function>

https://xsltfiddle.liberty-development.net/bEzkTcU


推荐阅读