XSLT增加变量

9
我有以下XML文件:
<data>
 <records>
   <record name="A record">
     <info>A1</info>
     <info>A2</info>
   </record>
   <record name="B record"/>
   <record name="C record">
     <info>C1</info>
   </record>
  </records>
</data>
我如何将其转换为以下输出?问题是如何计算在记录之间和记录/info之间的个数?
<div id="1">
    <p>A record</p>
    <span id="1">A1</span>
    <span id="2">A2</span> 
</div> 
<div id="2">
    <p>C record</p>   
    <span id="3">C1</span> 
</div>

好问题!(+1)。查看我的答案,它使用 XSLT 提供了一种简短的解决方案。 :) - Dimitre Novatchev
哦,@Alejandro 说这是唯一正确的! - Dimitre Novatchev
2个回答

7
解决方案1. 细粒度遍历。这个样式表:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="records">
        <xsl:apply-templates select="*[1]"/>
    </xsl:template>
    <xsl:template match="record"/>
    <xsl:template match="record[node()]">
        <xsl:param name="pRecordNum" select="1"/>
        <xsl:param name="pInfoNum" select="1"/>
        <div id="{$pRecordNum}">
            <xsl:apply-templates select="@*|*[1]">
                <xsl:with-param name="pInfoNum" select="$pInfoNum"/>
            </xsl:apply-templates>
        </div>
        <xsl:apply-templates select="following-sibling::record[node()][1]">
            <xsl:with-param name="pRecordNum" select="$pRecordNum +1"/>
            <xsl:with-param name="pInfoNum" select="$pInfoNum + count(info)"/>
        </xsl:apply-templates>
    </xsl:template>
    <xsl:template match="info">
        <xsl:param name="pInfoNum"/>
        <span id="{$pInfoNum}">
            <xsl:value-of select="."/>
        </span>
        <xsl:apply-templates select="following-sibling::info[1]">
            <xsl:with-param name="pInfoNum" select="$pInfoNum +1"/>
        </xsl:apply-templates>
    </xsl:template>
    <xsl:template match="@name">
        <p>
            <xsl:value-of select="."/>
        </p>
    </xsl:template>
</xsl:stylesheet>

输出:

<div id="1">
    <p>A record</p>
    <span id="1">A1</span>
    <span id="2">A2</span>
</div>
<div id="2">
    <p>C record</p>
    <span id="3">C1</span>
</div>
解决方案2: 使用preceding axe。这个样式表:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="record"/>
    <xsl:template match="record[node()]">
        <div id="{count(preceding-sibling::record[node()])+1}">
            <xsl:apply-templates select="@*|*"/>
        </div>
    </xsl:template>
    <xsl:template match="info">
        <span id="{count(preceding::info)+1}">
            <xsl:value-of select="."/>
        </span>
    </xsl:template>
    <xsl:template match="@name">
        <p>
            <xsl:value-of select="."/>
        </p>
    </xsl:template>
</xsl:stylesheet>

解决方案3: 使用fn:position()preceding轴。这个样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="records">
        <xsl:apply-templates select="record[node()]"/>
    </xsl:template>
    <xsl:template match="record">
        <div id="{position()}">
            <xsl:apply-templates select="@*"/>
            <xsl:apply-templates/>
        </div>
    </xsl:template>
    <xsl:template match="info">
        <span id="{count(preceding::info)+1}">
            <xsl:value-of select="."/>
        </span>
    </xsl:template>
    <xsl:template match="@name">
        <p>
            <xsl:value-of select="."/>
        </p>
    </xsl:template>
</xsl:stylesheet>

注意: 您需要使用显式拉取样式。

编辑: 忽略了span/@id的任何级别编号。


@Alejandro:很不错的努力。你可能会对另一种解决方案感兴趣 :) - Dimitre Novatchev
谢谢!太棒了。能否使用模板名称而不是apply-template?我该如何在模板名称中记录[node()]? - cc96ai
@cc96ai:首先,我编辑了这些样式表,因为我没有注意到您需要从任何级别对span/@id进行编号。请检查一下!我不明白为什么您要使用命名模板...请注意,使用call-template时,您不会更改上下文。您需要将位置和节点作为参数传递。但是然后您还需要迭代节点...我认为这不是一个好的解决方案。 - user357812

4

在XSLT中有一种简短的方法来实现这一点。使用<xsl:number>指令:

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

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

 <xsl:template match="record[info]">
   <xsl:variable name="vPos">
    <xsl:number count="record[info]"/>
   </xsl:variable>

   <div id="{$vPos}">
    <xsl:apply-templates/>
   </div>
 </xsl:template>

 <xsl:template match="info">
  <xsl:variable name="vPos">
   <xsl:number from="/" level="any" count="info" />
  </xsl:variable>
  <span id="{$vPos}"><xsl:apply-templates/></span>
 </xsl:template>
 <xsl:template match="record[not(info)]"/>
</xsl:stylesheet>

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

<data>
 <records>
   <record name="A record">
     <info>A1</info>
     <info>A2</info>
   </record>
   <record name="B record"/>
   <record name="C record">
     <info>C1</info>
   </record>
  </records>
</data>

期望的、正确的结果被产生:
<data>
    <records>
        <div id="1">
            <span id="1">A1</span>
            <span id="2">A2</span>
        </div>
        <div id="2">
            <span id="3">C1</span>
        </div>
    </records>
</data>

2
对于唯一正确的解决方案(我错过了跨度/ @id 应该来自任何级别。我会进行编辑),以及 XSLT 中为此任务制作的指令 xsl:number,请点赞 +1。 - user357812

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接