替换表列中的所有值

时间:2012-06-20 13:47:03

标签: xslt replace html-table xslt-2.0

如果列包含特定值(在本例中为“我想要你”),我试图在特定列中的所有节点中添加一个值(在本例中为字符串“I Added This”) )使用XSLT 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>

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

6 个答案:

答案 0 :(得分: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>

答案 1 :(得分: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>

您可以在this playground session运行它。

答案 2 :(得分: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' ,它是任何元素的子元素(因此thtd都匹配)这是tr的第二个子元素。此模板输出字符串'I Added This '的串联和当前(匹配)节点的字符串值。

  3. 请注意:这是一个XSLT 1.0解决方案,但它与XSLT 2.0处理器的工作方式相同,完全没有任何更改。将version的{​​{1}}属性更改为“2.0”是安全的(如果是nessecary)。

答案 3 :(得分:0)

谢谢Tim 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>

答案 4 :(得分: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>

答案 5 :(得分: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的doozy匹配:获取位置与<td>元素匹配的<th>元素中包含的所有文本(在最近的“标题”<tr>),其值为“我想要你”。对于此文本,返回一个新值,即“我添加此内容”和原始文本的串联。