XSLT:输出元素值取决于前后兄弟的值

时间:2014-07-01 12:54:38

标签: xml xslt xpath

我正在处理一些具有扁平结构的XML,其中通过重复的元素组隐式识别项目,而没有正确地嵌套在每个项目的单个父元素中(这是导致头痛的原因)。我需要使用XSLT将它转换为相同的版本,其中某些组使用不同的文本输出,具体取决于兄弟元素组中其中一个元素的值。所以,如果输入看起来像这样:

<items>

<item_title>Item 1</item_title>
<text1>Item 1 Text 1</text1>
<text2>Item 1 Text 2</text2>
<text3>Item 1 Text 3</text3>
<text4>Item 1 Text 4</text4>
<text5>Item 1 Text 5</text5>

<item_title>Item 2</item_title>
<text1>Item 2 Text 1</text1>
<flag>Suppress text</flag>
<text4>Item 2 Text 4</text4>
<text5>Item 2 Text 5</text5>

<item_title>Item 3</item_title>
<text1>Item 3 Text 1</text1>
<text3>Item 3 Text 3</text3>
<text4>Item 3 Text 4</text4>

<item_title>Item 4</item_title>
<text1>Item 4 Text 1</text1>
<text4>Item 4 Text 4</text4>
<text5>Item 4 Text 5</text5>

<item_title>Item 5</item_title>
<text2>Item 5 Text 2</text2>
<text3>Item 5 Text 3</text3>
<flag>Suppress text</flag>
<text5>Item 5 Text 5</text5>

</items>

我想输出这样的东西:

<items>

<item_title>Item 1</item_title>
<text1>Item 1 Text 1</text1>
<text2>Item 1 Text 2</text2>
<text3>Item 1 Text 3</text3>
<text4>Item 1 Text 4</text4>
<text5>Item 1 Text 5</text5>

<item_title>Item 2</item_title>
<text1>The text of this item has been suppressed.</text1>

<item_title>Item 3</item_title>
<text1>Item 3 Text 1</text1>
<text3>Item 3 Text 3</text3>
<text4>Item 3 Text 4</text4>

<item_title>Item 4</item_title>
<text1>Item 4 Text 1</text1>
<text4>Item 4 Text 4</text4>
<text5>Item 4 Text 5</text5>

<item_title>Item 5</item_title>
<text1>The text of this item has been suppressed.</text1>

</items>

我一直在困惑如何(实际上,是否)可以编写一个XPath表达式,为每个/ item_title标识它是否具有follow-sibling :: flag之前(按文档顺序)接下来是下一个-sibling :: item_title,然后对于每个文本元素,我可以将这个XPath表达式嵌入到一个XPath表达式中,该表达式标识元素的最接近的前一个兄弟:: item_title是否符合这个条件(在这种情况下元素不会得到输出) )。但我无法弄清楚如何。

这是通过我提到的方法,还是通过某种替代方法实现的?

这是一个问题,因为我正在处理垃圾XML,但这就是我所拥有的。

感谢您的任何建议。

2 个答案:

答案 0 :(得分:1)

如果您使用

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output indent="yes"/>

<xsl:key name="title" match="items/*[not(self::item_title)]" use="generate-id(preceding-sibling::item_title[1])"/>

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

<xsl:template match="items">
  <xsl:copy>
    <xsl:apply-templates select="item_title"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="items/item_title[key('title', generate-id())[self::flag[. = 'Suppress text']]]">
  <item title="{.}">
    <item1>The text of this item has been suppressed.</item1>
  </item>
</xsl:template>

<xsl:template match="items/item_title[not(key('title', generate-id())[self::flag[. = 'Suppress text']])]">
  <item title="{.}">
    <xsl:apply-templates select="key('title', generate-id())"/>
  </item>
</xsl:template>

</xsl:stylesheet>

你得到了

<items>
   <item title="Item 1">
      <text1>Item 1 Text 1</text1>
      <text2>Item 1 Text 2</text2>
      <text3>Item 1 Text 3</text3>
      <text4>Item 1 Text 4</text4>
      <text5>Item 1 Text 5</text5>
   </item>
   <item title="Item 2">
      <item1>The text of this item has been suppressed.</item1>
   </item>
   <item title="Item 3">
      <text1>Item 3 Text 1</text1>
      <text3>Item 3 Text 3</text3>
      <text4>Item 3 Text 4</text4>
   </item>
   <item title="Item 4">
      <text1>Item 4 Text 1</text1>
      <text4>Item 4 Text 4</text4>
      <text5>Item 4 Text 5</text5>
   </item>
   <item title="Item 5">
      <item1>The text of this item has been suppressed.</item1>
   </item>
</items>

如果您不想更正嵌套,请使用

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output indent="yes"/>

<xsl:key name="title" match="items/*[not(self::item_title)]" use="generate-id(preceding-sibling::item_title[1])"/>

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

<xsl:template match="items">
  <xsl:copy>
    <xsl:apply-templates select="item_title"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="items/item_title[key('title', generate-id())[self::flag[. = 'Suppress text']]]">
  <xsl:copy-of select="."/>
  <item1>The text of this item has been suppressed.</item1>

</xsl:template>

<xsl:template match="items/item_title[not(key('title', generate-id())[self::flag[. = 'Suppress text']])]">
  <xsl:copy-of select="."/>
  <xsl:apply-templates select="key('title', generate-id())"/>
</xsl:template>

</xsl:stylesheet>

答案 1 :(得分:1)

  

我一直在困惑如何(实际上,是否)可以编写一个XPath表达式,为每个/ item_title标识它是否具有follow-sibling :: flag之前(按文档顺序) next following-sibling :: item_title

Martin建议使用密钥作为一个整体来解决问题的更好方法,但是为了参考,可以通过汇总文档中所有以下兄弟flagitem_title元素的列表来完成此特定测试订购,然后看看第一个是否是一个标志:

<xsl:template match="item_title[
   (following-sibling::item_title | following-sibling::flag)[1][self::flag]]">