基于前兄弟处理指令对元素进行分组

时间:2014-05-06 14:33:20

标签: xslt xslt-1.0

我正在尝试使用一个键来组合<p>元素,这取决于它们是否具有特定的处理指令作为第一个兄弟姐妹,但我没有运气。

对于以下示例输入,我希望每个<p>具有包含“key”的第一个在前兄弟处理指令与其符合相同条件的同级<p>元素进行分组。示例输入:

<root>
    <p>not in key</p>
    <?samplePI key?>
    <p>start of key; </p>
    <?samplePI key?>
    <p>in key 1; </p>
    <?samplePI key?>
    <p>in key 2; </p>
    <p>Not in key</p>
    <?samplePI key?>
    <p>start of new key; </p>
    <?samplePI key?>
    <p>in new key 3;</p>
</root>

示例输出:

<root>
    <p>not in key</p>
    <p>start of key; in key 1; in key 2;</p>
    <p>Not in key</p>
    <p>start of new key; in new key 3;</p>
</root>

我所得到的一个例子:

<xsl:template match="root">
        <root>
            <xsl:apply-templates/>
        </root>
    </xsl:template>

    <xsl:template match="p">
        <xsl:choose>
            <xsl:when test="preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]][preceding-sibling::p[1][(preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]])]]">

            </xsl:when>
            <xsl:when test="preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]][preceding-sibling::p[1][not(preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]])]]">
                <p><xsl:value-of select="text()"/>
                    <xsl:apply-templates select="key('nodes', generate-id())" mode="groupedParas"/>
                </p>

            </xsl:when>
            <xsl:otherwise>
                <p><xsl:apply-templates/></p>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template match="p" mode="groupedParas">
        <xsl:apply-templates/>
    </xsl:template>


    <xsl:key name="nodes" match="node()[(self::p[preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]])]" use="generate-id((preceding-sibling::p)[last()])"/>

    <xsl:template match="text()">
        <xsl:value-of select="."/>
    </xsl:template>   

请注意,我需要帮助来获取正确的键语法,而不是生成所需的结构。我需要使用XSLT 1.0。任何帮助表示赞赏。

1 个答案:

答案 0 :(得分:3)

使用XSLT 2.0,它看起来很易于管理

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

<xsl:template match="root">
  <xsl:copy>
    <xsl:for-each-group select="*" group-adjacent="boolean(preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]])">
      <xsl:choose>
        <xsl:when test="current-grouping-key()">
          <p>
            <xsl:apply-templates select="current-group()/node()"/>
          </p>
        </xsl:when>
        <xsl:otherwise>
          <xsl:copy-of select="current-group()"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each-group>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet>

使用XSLT 1.0,我通常的做法是兄弟般的递归,但它需要经过漫长而复杂的匹配模式:

<?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="1.0">

<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>

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

<xsl:template match="processing-instruction()[contains(., 'key')]"/>

<xsl:template match="p[preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]
                       and preceding-sibling::node()[2][not(self::p[preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]])]]">
  <p>
    <xsl:apply-templates select="." mode="collect"/>
  </p>
</xsl:template>

<xsl:template match="p[preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]
                       and preceding-sibling::node()[2][self::p[preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]]]]"/>

<xsl:template match="p" mode="collect">
  <xsl:apply-templates/>
  <xsl:apply-templates select="following-sibling::node()[2][self::p and preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]]" mode="collect"/>
</xsl:template>

</xsl:stylesheet>

最后,由于您似乎想要使用密钥,上面显示的兄弟递归的变体使用key来标识一组p元素,如下所示:

<?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="1.0">

<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>

<xsl:key name="collect"

         match="p[preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]
                       and preceding-sibling::node()[2][self::p[preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]]]]"
         use="generate-id(preceding-sibling::p[preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]
                                               and not(preceding-sibling::node()[2][self::p[preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]]])][1])"/>

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

<xsl:template match="processing-instruction()[contains(., 'key')]"/>

<xsl:template match="p[preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]
                       and preceding-sibling::node()[2][not(self::p[preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]])]]">
  <p>
    <xsl:apply-templates select="./node() | key('collect', generate-id())/node()"/>
  </p>
</xsl:template>

<xsl:template match="p[preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]
                       and preceding-sibling::node()[2][self::p[preceding-sibling::node()[1][self::processing-instruction()[contains(., 'key')]]]]]"/>

</xsl:stylesheet>