【问题标题】:XSLT remove() functionXSLT remove() 函数
【发布时间】:2012-05-23 13:54:50
【问题描述】:

在 XSLT 中,有一个用于序列的 remove() 函数。给定一个序列和一个位置,它返回序列减去给定位置的项目。

问题是:如何在实际的 XSLT 文件中使用此函数?

我发现唯一提到的示例不仅仅是完全没有上下文的函数规范的反刍:http://books.google.com/books?id=W6SpffnfEPoC&pg=PA776&lpg=PA776&dq=xslt+%22remove+function%22&source=bl&ots=DQQrnXF_nB&sig=nrJtpEvYjBaZU0K8iAtdPTGUIbI&hl=en&sa=X&ei=QOq8T7aPDOyI6AHh-JBP&ved=0CEQQ6AEwAQ#v=onepage&q=xslt%20%22remove%20function%22&f=false

不幸的是,样式表示例位于第 777 和 778 页上,当然,这不包括在内。我不拥有那本书。

那么,有人有在实际样式表中使用remove() XSLT 函数的示例吗?

编辑:让我们提供一个更具体的例子,好吗?

我在 XSLT 中有一个序列。此序列由文本文件中的所有行组成。

<xsl:variable name="lines" select="tokenize(unparsed-text($filePath), '\r?\n')" />

这些行中的每一行都是一个记录...除了一个,它给了我记录数。所以我有以下代码来查找该行:

<xsl:variable name="recordCount">
  <xsl:for-each select="$lines[position()]">
    <xsl:variable name="i" select="position()" />
    <xsl:analyze-string select="$lines[$i]" regex="RECORD COUNT = \d+">
      <xsl:matching-substring>
        <xsl:value-of select="replace($lines[$i], '[^0-9]', '')" />
      </xsl:matching-substring>
    </xsl:analyze-string>
  </xsl:for-each>
</xsl:variable>

在开始循环遍历这些行以获取所有实际记录之前,我执行了上述操作,因此我的目标是在找到时从$lines 序列中删除“记录计数”行。这样,当我循环抓取记录时,我不必每次询问“这实际上不是记录,而是记录计数行吗?你知道,我已经寻找并找到的那个东西吗? "

编辑 (2): 基于 Martin Honnen 的回答,我的最终 XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <!-- I want to produce an XML document. -->
  <xsl:output method="xml" indent="yes" />

  <!-- Path to input text file. -->
  <xsl:param name="filePath" select="TestFile.txt" />

  <!-- Regex in replace() removes leading and trailing blank space. -->
  <xsl:variable name="text" select="replace(unparsed-text($filePath), '(^[\r\n]*\s*[\r\n]+)|([\r\n]+\s*[\r\n]*$)', '')" />

  <!-- Regex in tokenize() sets the delimiter to be any blank space between record lines. -->
  <!-- This effectively removes any blank lines. -->
  <xsl:variable name="lines" select="tokenize($text, '[\r\n]+\s*[\r\n]*')" />

  <!-- This finds the "RECORD COUNT = ?" line. -->
  <xsl:variable name="recordCountIndex"
    select="for $pos in 1 to count($lines) return $pos[matches($lines[$pos], 'RECORD COUNT = \d+')]" />

  <!-- Regex in replace() strips everything that's not a number, leaving only the numeric count. -->
  <!-- Example: "RECORD COUNT = 25" -> "25" -->
  <xsl:variable name="recordCount" select="replace($lines[$recordCountIndex], '[^0-9]', '')" />

  <xsl:template name="main">
    <root>
      <recordCount>
        <!-- The record count value being inserted. -->
        <xsl:value-of select="$recordCount" />
      </recordCount>
      <records>
        <!-- Iterate over the $lines minus the line containing the record count. -->
        <xsl:for-each select="remove($lines, $recordCountIndex)">
          <!-- Items in each record, split by blank space. -->
          <!-- Example: "a b c" -> "[a, b, c]" -->
          <xsl:variable name="record" select="tokenize(., ' ')[position()]" />
          <record>
            <aThing>
              <xsl:value-of select="$record[1]" />
            </aThing>
            <aDifferentThing>
              <xsl:value-of select="$record[2]" />
            </aDifferentThing>
            <someStuff>
              <xsl:value-of select="$record[3]" />
            </someStuff>
          </record>
        </xsl:for-each>
      </records>
    </root>
  </xsl:template>
</xsl:stylesheet>

【问题讨论】:

    标签: xml xslt-2.0


    【解决方案1】:

    <xsl:variable name="seq1" select="1, 2, 3, 4"/>
    <xsl:variable name="seq2" select="remove($seq1, 2)"/>
    

    使变量 seq2 的值成为三个数值 1、3、4 的序列。

    [编辑]

    这是一个基于您编辑的问题描述的示例:

    <?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"
      exclude-result-prefixes="xs"
      version="2.0">
    
      <xsl:output method="text"/>
    
      <xsl:param name="filePath" select="'test2012052301.txt'"/>
    
      <xsl:variable name="lines" select="tokenize(unparsed-text($filePath), '\r?\n')" />
    
      <xsl:variable name="index" as="xs:integer"
        select="for $pos in 1 to count($lines) return $pos[matches($lines[$pos], 'RECORD COUNT = [0-9]+')]"/>
    
      <xsl:variable name="recordCount" as="xs:integer"
        select="xs:integer(replace($lines[$index], '[^0-9]', ''))"/>
    
      <xsl:template name="main">
        <xsl:value-of select="remove($lines, $index)" separator="&#10;"/>
        <xsl:text>count is: </xsl:text>
        <xsl:value-of select="$recordCount"/>
      </xsl:template>
    
    </xsl:stylesheet>
    

    以文本文件为例

    foo
    bar
    RECORD COUNT = 3
    baz
    

    样式表输出

    foo
    bar
    baz
    count is: 3
    

    [编辑2] 我认为你可以缩短部分

      <records>
        <!-- The $lines sequence trimmed down to only consist of valid records. -->
        <!-- (I have found no way around having this intermediate variable.) -->
        <xsl:variable name="records" select="remove($lines, $recordCountIndex)" />
        <xsl:for-each select="$records[position()]">
          <!-- Variable for iteration. Perhaps there's a more elegant way to do this. -->
          <xsl:variable name="i" select="position()" />
          <!-- Items in each record, split by blank space. -->
          <!-- Example: "a b c" -> "[a, b, c]" -->
          <xsl:variable name="recordItems" select="tokenize($records[$i], ' ')" />
          <record>
            <item1>
              <xsl:value-of select="$recordItems[1]" />
            </item1>
            <item2>
              <xsl:value-of select="$recordItems[2]" />
            </item2>
            <item3>
              <xsl:value-of select="$recordItems[3]" />
            </item3>
          </record>
        </xsl:for-each>
      </records>
    

    你的样式表到

      <records>
        <xsl:for-each select="remove($lines, $recordCountIndex)">
          <record>
            <xsl:for-each select="tokenize(., ' ')[position() lt 4]">
              <xsl:element name="item{position()}">
                <xsl:value-of select="."/>
              </xsl:element>
            </xsl:for-each>
          </record>
        </xsl:for-each>
      </records>
    

    实际上,谓词position() lt 4 仅在一行可以包含三个以上标记时才需要。

    作为说明,我现在在您的帖子中两次看到类似for-each select="$records[position()] 的构造,带有[position()] 的谓词完全没用,您可以简单地使用for-each select="$records"

    【讨论】:

    • 所以我必须做一个中间变量?在您的示例中,有没有办法用 remove() 调用的结果覆盖 seq1,而不必创建 seq2 变量?
    • 您根本不必使用变量,但由于您的帖子没有显示任何输入数据,因此我必须弥补一些数据,并且最短的完整样本似乎正在使用变量。至于覆盖变量,这在 XSLT 之类的声明性语言中是不可能的,无论您是否使用该特定函数。如果您想要一个不使用变量来存储函数结果的示例,请使用例如&lt;xsl:value-of select="remove($seq1, 2)" separator=", "/&gt; 输出在序列上调用该函数的结果。
    • 添加了一些示例,希望能更清楚地解释我的难题。
    • 我的最新编辑试图展示如何简化您的代码,并展示了如何使用删除功能。我只是输出获得的序列,当然如果需要/需要,您可以for-each 覆盖它。
    • 我的最新编辑显示了我最终的 XSLT。除非您(或其他任何人)知道如何使用中间“记录”变量来存储从 remove() 调用返回的序列,或者在接下来的几天内有更优雅的方式来执行迭代器,否则我将当我从假期周末回来时接受你的回答。
    【解决方案2】:

    很难准确找出你的困惑所在。

    首先,从序列中删除项目永远不会从树中删除节点。 (我反对规范谈论“包含节点”的序列的方式;我认为最好将它们视为包含对节点的引用。因此,您要删除对节点的引用,这不会影响节点以任何方式本身。)

    其次,您似乎在考虑变量,因为它们有时在过程语言中被描述为包含值的命名框,这些值可以在不同时间包含不同的值。不要以这种方式考虑 XSLT 和 XQuery 变量:将它们视为命名值。 “覆盖”只是没有意义的操作。

    最后,用例。我使用 remove 的最常见方法是获取序列的尾部:remove($seq, 1)。您也可以将其写为subsequence($seq, 2)$seq[position() gt 1],但remove() 的击键次数更少。老实说,我想不出一个现实生活中的例子,我用任何其他方式使用remove(),我想不出一个。

    这使我对您的问题进行了观察。问“我如何使用这个功能”是一个非常奇怪的问题。我们期望人们问的是“我该如何解决这个问题”。有时,当人们询问如何使用某个功能时,他们正在努力解决一个特定的问题,但他们并没有告诉我们问题是什么。如果您告诉我们会有所帮助:remove() 很有可能不是解决方案的一部分。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-21
      • 2017-12-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多