3

我正在尝试使用 XSLT 向特定列中的所有节点添加一个值(在本例中为字符串“I added This”),如果该列确实包含某个值(在本例中为文本“I Want You”) 2.0。

这意味着,如果我有下表:

<table>
        <tr>
            <th>header value</th>
            <th>I Want You</th>
            <th>header value</th>
        </tr>

        <tr>
            <td>value 1</td>
            <td>value 2</td>
            <td>value 3</td>
        </tr>

        <tr>
            <td>value 4</td>
            <td>value 5</td>
            <td>value 6</td>
        </tr>
</table>

应用 xslt 脚本后,输出应该是这样的:

<table>
        <tr>
            <th>header value</th>
            <th>I Want You</th>
            <th>header value</th>
        </tr>

        <tr>
            <td>value 1</td>
            <td>I Added This value 2</td>
            <td>value 3</td>
        </tr>

        <tr>
            <td>value 4</td>
            <td>I Added This value 5</td>
            <td>value 6</td>
        </tr>
</table>

为了实现这一点,我编写了许多不同的样式表,但我什至没有接近:

复制整个表:

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

这让我得到了我想要更改的列的值 - 但从那里我不知道如何在每个 td 值前面加上“我添加了这个值”这个短语

<xsl:template match="//th[contains(lower-case(.), 'I Want You')]">

    <xsl:variable name="tdPos" select="count(preceding-sibling::td)+2"/>

    <xsl:for-each select="current()/parent::*/following-sibling::tr">
        <xsl:value-of select="./td[$tdPos]/text()"/>
    </xsl:for-each>
</xsl:template>

非常感谢任何可能将我指向正确方向的提示!

4

6 回答 6

1

一个值得注意的问题是您的“包含”测试不太正确

<xsl:template match="//th[contains(lower-case(.), 'I Want You')]"> 

大概是下面这个

<xsl:template match="//th[contains(lower-case(.), lower-case('I Want You'))]"> 

但是,如果您将搜索值参数化,则可能会更好,尽管您无法在模板匹配中使用此类参数。相反,您只需匹配任何单元格......

<xsl:template match="tr/*">

然后您可以测试前一行上的等效列是否具有您想要的值

<xsl:if 
   test="../preceding-sibling::tr/*[$position][contains(., $match)]">

(为简洁起见删除了小写()命令)

这里 $position 将是一个包含当前单元格位置的变量,而 $match 是您要查找的变量。

尝试以下 XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="html" indent="yes"/>
   <xsl:param name="match" select="'I Want You'" />
   <xsl:param name="add" select="'I Added This'" />

   <xsl:template match="tr/*">
      <xsl:variable name="position" select="count(preceding-sibling::*) + 1"/>          <xsl:copy>
         <xsl:apply-templates select="@*"/>
         <xsl:if test="../preceding-sibling::tr/*[$position][contains(lower-case(.), lower-case($match))]">
            <xsl:value-of select="concat($add, ' ')" />
         </xsl:if>
         <xsl:apply-templates select="text()"/>
      </xsl:copy>
   </xsl:template>

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

当应用于您的 XML 时,将生成以下内容

<table>
<tr>
   <th>header value</th>
   <th>I Want You</th>
   <th>header value</th>
</tr>
<tr>
   <td>value 1</td>
   <td>I Added This value 2</td>
   <td>value 3</td>
</tr>
<tr>
   <td>value 4</td>
   <td>I Added This value 5</td>
   <td>value 6</td>
</tr>
</table>

当应用于这个 XML

<table>
   <tr>
      <th>header value</th>
      <th>header value</th>
      <th>header value</th>
   </tr>
   <tr>
      <td>value 1</td>
      <td>I Want You</td>
      <td>value 2</td>
   </tr>
   <tr>
      <td>value 3</td>
      <td>value 4</td>
      <td>value 5</td>
   </tr>
</table>

以下是输出

<table>
<tr>
   <th>header value</th>
   <th>header value</th>
   <th>header value</th>
</tr>
<tr>
   <td>value 1</td>
   <td>I Want You</td>
   <td>value 2</td>
</tr>
<tr>
   <td>value 3</td>
   <td>I Added This value 4</td>
   <td>value 5</td>
</tr>
</table>
于 2012-06-20T14:26:41.877 回答
1

您在正确的路线上 - 您只需要在输出节点时执行一个条件,以便在 TD 节点的情况下,检查同一索引处的表亲 TH 节点是否具有所需的文本。

<xsl:template match="*|@*">
    <xsl:variable name='curr_pos' select='position()' />
    <xsl:copy>
        <xsl:apply-templates select='@*' />
        <xsl:if test='name() = "td" and /table/tr[1]/th[$curr_pos] = "I Want You"'>prefix - </xsl:if>
        <xsl:value-of select='text()' />
        <xsl:apply-templates select='*' />
    </xsl:copy>
</xsl:template>

您可以在这个游乐场会话中运行它。

于 2012-06-20T14:42:13.490 回答
1

可能是最短和最简单的转换(没有preceding-sibling::,没有position(),没有条件指令,没有变量)

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

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

 <xsl:template match="tr/*[2]/text()[not(. = 'I Want You')]">
  <xsl:value-of select="concat('I Added This ', .)"/>
 </xsl:template>
</xsl:stylesheet>

当此转换应用于提供的 XML 文档时:

<table>
    <tr>
        <th>header value</th>
        <th>I Want You</th>
        <th>header value</th>
    </tr>
    <tr>
        <td>value 1</td>
        <td>value 2</td>
        <td>value 3</td>
    </tr>
    <tr>
        <td>value 4</td>
        <td>value 5</td>
        <td>value 6</td>
    </tr>
</table>

产生了想要的正确结果:

<table>
   <tr>
      <th>header value</th>
      <th>I Want You</th>
      <th>header value</th>
   </tr>
   <tr>
      <td>value 1</td>
      <td>I Added This value 2</td>
      <td>value 3</td>
   </tr>
   <tr>
      <td>value 4</td>
      <td>I Added This value 5</td>
      <td>value 6</td>
   </tr>
</table>

说明

  1. 身份规则“按原样”复制每个匹配的节点。

  2. 有一个覆盖模板。它匹配字符串值不是字符串的任何文本节点,'I Want You' 并且该节点是任何元素的子节点(因此两者都匹配),th并且td是 a 的第二个子元素tr。此模板输出字符串的串联'I Added This '和当前(匹配)节点的字符串值。

请注意:这是一个 XSLT 1.0 解决方案,但它与 XSLT 2.0 处理器的工作原理相同,完全没有任何变化。version将 的属性更改xsl:stylesheet为“2.0”是安全的(如果需要) 。

于 2012-06-21T02:47:01.253 回答
0

谢谢蒂姆C!它就像一个魅力,它是一个非常好的解决方案!虽然如果文档中有多个表格我会遇到一些小问题,但我会在上面应用此样式表。它还会在不包含“我想要你”值的表中添加值。有什么解决方法吗?我尝试选择所有表,然后单独运行其列,但这导致没有输出。

<xsl:template match="//table">
  <xsl:for-each select="/tr/*">
    <xsl:variable name="position" select="position()" />
    <xsl:copy>
        <xsl:apply-templates select="@*"/>
        <xsl:if test="../preceding-sibling::tr/*[position() = $position][contains(lower-case(.), lower-case($match))]">
            <xsl:value-of select="concat($add, ' ')" />
        </xsl:if>
        <xsl:apply-templates select="text()"/>
    </xsl:copy>
  </xsl:for-each>
</xsl:template>
于 2012-06-20T15:09:18.060 回答
0

此样式表(也适用于 XSLT 1.0)...

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>

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

  <xsl:template match="td">
  <xsl:variable name="col" select="count(preceding-sibling::td)+1" /> 
  <xsl:copy>
   <xsl:apply-templates select="@*"/>
   <xsl:if test="contains(../../tr[1]/th[$col], 'I Want You')">
     <xsl:value-of select="'I Added This '" /> 
   </xsl:if>  
   <xsl:apply-templates select="node()"/>
 </xsl:copy>
</xsl:template>

</xsl:stylesheet>

...当应用于您的样本输入时,将产生您所需的输出。

蒂姆的回答是不正确的,或者至少不是始终正确的。position() 函数很棘手。问题是 position() 包括空白节点和您可能在 html 表中拥有的其他非表内容,例如微数据格式。如果标题行中的空白与数据行不同,则 Tim C 的解决方案将不起作用。count() 是必需的。

我故意留下了小写()位。我将测试的琐碎细节留给 OP。

这里还有一个更简单的 XSLT 2.0 解决方案......

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>

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

  <xsl:template match="td[
     for $col in position() return
       contains(../../tr[1]/th[$col], 'I Want You')]">
  <xsl:copy>
   <xsl:apply-templates select="@*"/>
   <xsl:value-of select="'I Added This '" /> 
   <xsl:apply-templates select="node()"/>
 </xsl:copy>
</xsl:template>

</xsl:stylesheet>
于 2012-06-20T15:16:20.790 回答
0

这是一个简短而正确的答案,它可以处理任何形式的“我想要这个”,<th>并且可以在具有大量此类表格的文档中使用(如果它们在语义上是正确的)。

当这个 XSLT:

<?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="no" indent="yes"/>
  <xsl:strip-space elements="*"/>

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

  <!-- Template #2 -->
  <xsl:template match="tr[position() &gt; 1]/td[position() = count(../../th[. = 'I Want You']/preceding-sibling::th) + 1]/text()">
    <xsl:value-of select="concat('I Added This ', .)" />
  </xsl:template>

</xsl:stylesheet>

应用于此 XML 文档:

<table>
  <tr>
    <th>header value</th>
    <th>I Want You</th>
    <th>header value</th>
  </tr>
  <tr>
    <td>value 1</td>
    <td>value 2</td>
    <td>value 3</td>
  </tr>
  <tr>
    <td>value 4</td>
    <td>value 5</td>
    <td>value 6</td>
  </tr>
</table>

产生了想要的结果:

<?xml version="1.0" encoding="UTF-8"?>
<table>
  <tr>
    <th>header value</th>
    <th>I Want You</th>
    <th>header value</th>
  </tr>
  <tr>
    <td>value 1</td>
    <td>I Added This value 2</td>
    <td>value 3</td>
  </tr>
  <tr>
    <td>value 4</td>
    <td>I Added This value 5</td>
    <td>value 6</td>
  </tr>
</table>

请注意,如果您<table>的文档中有多个这样的元素,那么这个 XSLT 也涵盖了这种情况。这不是您特别要求的,但它不在可能性范围之外,所以我想我会涵盖它。

XML:

<body>
  <table>
    <tr>
      <th>header value</th>
      <th>I Want You</th>
      <th>header value</th>
    </tr>
    <tr>
      <td>value 1</td>
      <td>value 2</td>
      <td>value 3</td>
    </tr>
    <tr>
      <td>value 4</td>
      <td>value 5</td>
      <td>value 6</td>
    </tr>
  </table>
  <table>
    <tr>
      <th>I Want You</th>
      <th>header value</th>
      <th>header value</th>
    </tr>
    <tr>
      <td>value 1</td>
      <td>value 2</td>
      <td>value 3</td>
    </tr>
    <tr>
      <td>value 4</td>
      <td>value 5</td>
      <td>value 6</td>
    </tr>
  </table>
</body>

结果:

<?xml version="1.0" encoding="UTF-8"?><body>
  <table>
    <tr>
      <th>header value</th>
      <th>I Want You</th>
      <th>header value</th>
    </tr>
    <tr>
      <td>value 1</td>
      <td>I Added This value 2</td>
      <td>value 3</td>
    </tr>
    <tr>
      <td>value 4</td>
      <td>I Added This value 5</td>
      <td>value 6</td>
    </tr>
  </table>
  <table>
    <tr>
      <th>I Want You</th>
      <th>header value</th>
      <th>header value</th>
    </tr>
    <tr>
      <td>I Added This value 1</td>
      <td>value 2</td>
      <td>value 3</td>
    </tr>
    <tr>
      <td>I Added This value 4</td>
      <td>value 5</td>
      <td>value 6</td>
    </tr>
  </table>
</body>

解释:

  1. 模板 #1 - 身份模板- 按原样复制所有内容。我们在后续模板中覆盖此模板。

  2. 模板#2——这个模板匹配了一个XPath:获取包含在<td>元素中的所有文本,其位置与值为“I Want You”的<th>元素(在最近的“header”中)匹配。<tr>对于这个文本,返回一个新值,它是“我添加了这个”和原始文本的连接。

于 2012-06-21T21:00:37.870 回答