preceding轴的使用实例

以前很讨厌轴这个概念,但最近却用它解决了几个问题,认识上也有了提高,记录一下。

原XML文档:

<?xml version="1.0" encoding="UTF-8"?>
<root>
 <customer>
  <name>Neo</name>
  <amount>20</amount>
  <description>amount using calculation 1</description>
 </customer>
 <customer>
  <name>Neo</name>
  <amount>30</amount>
  <description>amount using calculation 2</description>
 </customer>
 <customer>
  <name>Mark</name>
  <amount>40</amount>
  <description>amount using calculation 3</description>
 </customer>
 <customer>
  <name>Neo</name>
  <amount>50</amount>
  <description>amount using calculation 4</description>
 </customer>
 <customer>
  <name>Mark</name>
  <amount>60</amount>
  <description>amount using calculation 5</description>
 </customer>
 <customer>
  <name>Meddy</name>
  <amount>70</amount>
  <description>amount using calculation 6</description>
 </customer>
</root>
 在root的根目录下有多个 customer节点,每个customer节点有name,amout,description三个子节点,现在的问题是想输出一个HTML表格,相同的 name 只显示一次,不同的amount和description显示在 name下面。

也就是

Nero

20     amount using calculation 1

30    amount using calculation 2

50    amount using calculation 4

mark

...............

初始的想法是,遍历每个节点,找到所有不同的name,然后对每个name,执行一次输出操作。

但是后来发现XSLT没有数组,即使用字符串,也不太好处理。最终使用preceding轴解决了这个问题:

那就是在第一次出现一个name的时候,执行一次输出操作。

具体代码:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="GB2312" indent="yes"/>
<xsl:template match="/">
<html>
<head>
<title>Result</title>
</head>
<body>
<table>
<xsl:for-each select="root/customer">
 <xsl:if test="not(preceding-sibling::customer[name=current()/name])">

   <!--前驱轴上不存在name跟当前节点的name相同的customer节点。-->
  <tr>
   <xsl:apply-templates select="name"/>
  </tr>
  <xsl:for-each select="../customer[name=current()/name]">
   <tr>
    <xsl:apply-templates select="amount"/>
    <xsl:apply-templates select="description"/>
   </tr>
  </xsl:for-each>
    </xsl:if> 
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="amount|description|name">
  <td>
   <xsl:value-of select="."/>
  </td>
</xsl:template>
</xsl:stylesheet>

呵呵,解决了问题,后来想到,让输出结果,按name的升序排列,加一个排序规则即可。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="GB2312" indent="yes"/>
<xsl:template match="/">
<html>
<head>
<title>Result</title>
</head>
<body>
<table>
<xsl:for-each select="root/customer">
 <xsl:sort select="name"/>
 <xsl:if test="not(preceding-sibling::customer[name=current()/name])">
  <tr>
   <xsl:apply-templates select="name"/>
  </tr>
  <xsl:for-each select="../customer[name=current()/name]">
   <tr>
    <xsl:apply-templates select="amount"/>
    <xsl:apply-templates select="description"/>
   </tr>
  </xsl:for-each>
    </xsl:if> 
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="amount|description|name">
  <td>
   <xsl:value-of select="."/>
  </td>
</xsl:template>
</xsl:stylesheet>

顺便理解了sort的用法,原来sort只要加在循环或者模板中,就可以把所选定的节点集合看成已经排好序的了。

另外一个问题:

XML原文件:

<?xml version="1.0"?>
<list>
 <message>
  <header>0001</header>
  <lines>
   <line>aaaa</line>
   <line>bbbb</line>
   <line>aaaa</line>
   <line>ffff</line>
  </lines>
  <lines>
   <line>aaaa</line>
   <line>message1 group2 line2 </line>
   <line>bbbb</line>
  </lines>
 </message>
 <message>
  <header>0002</header>
  <lines>
   <line>aaaa</line>
   <line>message2 group1 line2 </line>
   <line>aaaa</line>
   <line>message2 group1 line4 </line>
  </lines>
  <lines>
   <line>message2 group2 line1 </line>
   <line>aaaa</line>
  </lines>
  <lines>
   <line>message2 group3 line1 </line>
   <line>aaaa</line>
   <line>aaaa</line>
  </lines>
 </message>
 <message>
  <header>0003</header>
  <lines>
   <line>message3 group2 line1 </line>
   <line>message2 group2 line2 </line>
  </lines>
  <lines>
   <line>message3 group3 line1 </line>
   <line>aaaa</line>
   <line>aaaa</line>
  </lines>
 </message>
</list>
也就是说,一个list下有多个message,每个message有一个header和多个lines,每个lines下有多个line.现在的问题是,输出每个message中line为aaaa的节点序号,该序号是aaaa的line在message中出现的序号。输出结果像像下边所示:

  0001:1  2  3 
  0002:1  2  3  4  5 
  0003:1  2 

考虑用preceding轴,求每个aaaa的line前有多少这样的节点,但是,前面的message的line节点也会被记录在内,又想到每个line和另外message的line虽然名字相同,但是路径不同。

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

<xsl:template match="/">
 <xsl:for-each select="list/message">
  <xsl:text>
  </xsl:text>
  <xsl:value-of select="concat(header,':')"/>
  <xsl:for-each select="lines/line[. = 'aaaa']">
   <xsl:value-of select="count(preceding::line[. = 'aaaa']) - count(preceding::message/lines/line[. = 'aaaa'])+1"/>
   <xsl:text>  </xsl:text>
  </xsl:for-each>
 </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

问题解决。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值