XSLT - 分析以下连续节点

时间:2016-03-29 00:50:58

标签: xml xslt xslt-2.0

我有一个像这样的xml,

<doc>
    <para>texttext<page>1</page>texttext<page>1</page>texttext</para>
    <para>texttext<page>1</page><page>2</page>texttext</para>
    <para>texttext<page>1</page><page>2</page><page>3</page>texttext<page>4</page><page>5</page><page>6</page>texttext</para>
    <para>texttext<page>1</page><page>2</page><page>3</page><page>4</page>texttext</para>
</doc>

我需要使用xsl转换将<page>个节点转换为<link>,并且需要考虑以下规则,

  • 如果只显示一个<page>节点(未跟随任何页面节点),则只转换为<link>
  • 如果连续放置了两个<page>节点(上例中的方案2)&#39;,&#39;必须在输出<link>节点
  • 之间添加
  • 如果连续放置了3个或更多<page>个节点(上例中的方案3和4),只需添加由&#39;分隔的页面节点的第一个和最后一个内容 - &#39;

所以,输出应该是这样的,

<doc>
    <para>texttext<link>1</link>texttext<link>1</link>texttext</para>
    <para>texttext<link>1</link>,<link>2</link>texttext</para>
    <para>texttext<link>1</link>-<link>3</link>texttext<link>4</link>-<link>6</link>texttext</para>
    <para>texttext<link>1</link>-<link>4</link>texttext</para>
</doc>

我写了以下xsl来执行此任务,

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

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

    <xsl:template match="page[following-sibling::node()[1][self::page]]">
        <link>
            <xsl:apply-templates/>
        </link>
        <xsl:text>,</xsl:text>
        <link>
            <xsl:apply-templates select="following-sibling::*[1]"/>
        </link>
    </xsl:template>

    <xsl:template match="page[following-sibling::node()[1][self::page]][following-sibling::node()[2][self::page]]">
        <link>
            <xsl:apply-templates/>
        </link>
        <xsl:text>-</xsl:text>
        <link>
            <xsl:apply-templates select="following-sibling::*[2]"/>
        </link>
    </xsl:template>

但是这种方法不是因为它添加了&#39;,&#39;如果出现3个连续<page>个节点,并且连续出现更多<page>个节点,则此方法效率不高。

任何人都可以在xslt中建议一个好的方法来分析xslt后面的兄弟姐妹并执行此任务..

1 个答案:

答案 0 :(得分:2)

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

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

    <xsl:template match="page[not(preceding-sibling::node()[1][self::page])]">
        <xsl:variable name="pages" select="following-sibling::page[
            preceding-sibling::node()[1][self::page]
            and generate-id(current()) = generate-id(preceding-sibling::page[
                not(preceding-sibling::node()[1][self::page])
            ][1])
        ]" />
        <xsl:apply-templates select="." mode="link" />
        <xsl:if test="count($pages) = 1">,</xsl:if>
        <xsl:if test="count($pages) &gt; 1">-</xsl:if>
        <xsl:apply-templates select="$pages[last()]" mode="link" />
    </xsl:template>
    <xsl:template match="page" />

    <xsl:template match="page" mode="link">
        <link>
            <xsl:apply-templates select="@*|node()"/>
        </link>
    </xsl:template>
</xsl:transform>

结果

<doc>
    <para>texttext<link>1</link>texttext<link>1</link>texttext</para>
    <para>texttext<link>1</link>,<link>2</link>texttext</para>
    <para>texttext<link>1</link>-<link>3</link>texttext<link>4</link>-<link>6</link>texttext</para>
    <para>texttext<link>1</link>-<link>4</link>texttext</para>
</doc>

下面,

<xsl:template match="page[not(preceding-sibling::node()[1][self::page])]">

匹配开始&#34;范围&#34;的任何<page>连续页面。

选择连续范围的剩余页面有点棘手,但可以这样做:

    以下所有兄弟页面中的
  • ,选择那些
    • 本身紧接着<page>(即&#34;范围的一部分&#34; )和
    • 最接近的前一个<page>,其前面没有另一个<page>(即&#34;最接近<page>的范围&#34; )与当前节点相同。

鉴于我们只处理在此模板中开始范围的<page>个节点,这相当于&#34;是当前范围的一部分&#34;

在XPath术语中,如上所示:

following-sibling::page[
    preceding-sibling::node()[1][self::page]
    and generate-id(current()) = generate-id(preceding-sibling::page[
        not(preceding-sibling::node()[1][self::page])
    ][1])
]