我需要一些类似"LEFT JOIN"的XSLT语句:如果所选节点确实存在,则返回所有这样的节点,否则只循环一次。
这与xsl:for-each循环不同,因为当不存在这样的节点时,for-each循环返回0行。
下面是一个实际的例子。
XML文件:<root>
<sec1>
<x1/> ... <x1/>
</sec1>
<sec2>
<x2/> ... <x2/>
</sec2>
...
<sec10>
<x10/> ... <x10/>
</sec10>
</root>
现在,我不知道有多少"x1","x2",…我有x10,我想打印出所有可能的组合。一个简单而错误的解决方案:
<xsl:for-each select="/root/sec1/x1">
<xsl:for-each select="/root/sec2/x2">
...
<xsl:for-each select="/root/sec10/x10">
...print x1 and x2... and x10
</xsl:for-each>
...
</xsl:for-each>
</xsl:for-each>
这个解决方案是错误的,因为,如果没有"x3"它返回0行(就像一个完整的JOIN),而我想看到所有其他值(如左或右JOIN)。
我可以组合使用xsl:choose、xls:when、xsl:foreach和xsl: else,但是这很长。
我已经尝试构建我自己的xsl模板,但它不工作:
<xsl:template name="left-join">
<xsl:param name="select"/>
<xsl:param name="template"/>
<xsl:choose>
<xsl:when test="$select">
<xsl:for-each select="$select">
<xsl:call-template name="$templatename"> <!--WRONG -->
<xsl:with-param name="one-parameter" select="$select"/>
</xsl:call-template>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="$templatename">
<xsl:with-param name="one-parameter" select="$select"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
即使我不完全理解你的问题,我也会试着回答。我的理解是,您锁定的是sql左连接之类的东西。(例如,http://www.w3schools.com/sql/sql_join_left.asp)XML/XSLT版本可能如下所示。
输入数据: <root>
<Persons>
<Person id="1">
<Name>Hansen</Name>
</Person>
<Person id="2">
<Name>Svendson</Name>
</Person>
<Person id="3">
<Name>Pettersen</Name>
</Person>
</Persons>
<Orders>
<Order id="1" >
<P_Id>3</P_Id>
<OrderNo>77895</OrderNo>
</Order>
<Order id="2">
<P_Id>3</P_Id>
<OrderNo>44678</OrderNo>
</Order>
<Order id="3">
<P_Id>1</P_Id>
<OrderNo>22456</OrderNo>
</Order>
<Order id="4">
<P_Id>1</P_Id>
<OrderNo>24562</OrderNo>
</Order>
<Order id="5">
<P_Id>15</P_Id>
<OrderNo>34764</OrderNo>
</Order>
</Orders>
</root>
XSLT <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="root">
<xsl:call-template name="person_order" />
</xsl:template>
<xsl:template name="person_order">
<orders>
<xsl:for-each select="//Person">
<xsl:variable name ="pid" select="@id" />
<xsl:call-template name="left_join">
<xsl:with-param name="jname" select="'order'"/>
<xsl:with-param name="left" select="."/>
<xsl:with-param name="right" select="//Order[P_Id = $pid]"/>
</xsl:call-template>
</xsl:for-each>
</orders>
</xsl:template>
<xsl:template name="left_join">
<xsl:param name="jname" />
<xsl:param name="left" />
<xsl:param name="right" />
<xsl:choose>
<xsl:when test="$right">
<xsl:for-each select="$right">
<xsl:call-template name="print_join">
<xsl:with-param name="jname" select="$jname"/>
<xsl:with-param name="left" select="$left"/>
<xsl:with-param name="right" select="."/>
</xsl:call-template>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="print_join">
<xsl:with-param name="jname" select="$jname"/>
<xsl:with-param name="left" select="$left"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*"/>
</xsl:copy>
</xsl:template>
<xsl:template name="print_join">
<xsl:param name="jname" />
<xsl:param name="left" />
<xsl:param name="right" />
<xsl:element name="{$jname}" >
<xsl:for-each select="$left">
<xsl:apply-templates select="node() "/>
</xsl:for-each>
<xsl:if test="$right">
<xsl:for-each select="$right">
<xsl:apply-templates select="node() "/>
</xsl:for-each>
</xsl:if>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
生成输出:
<orders>
<order>
<Name>Hansen</Name>
<P_Id>1</P_Id>
<OrderNo>22456</OrderNo>
</order>
<order>
<Name>Hansen</Name>
<P_Id>1</P_Id>
<OrderNo>24562</OrderNo>
</order>
<order>
<Name>Svendson</Name>
</order>
<order>
<Name>Pettersen</Name>
<P_Id>3</P_Id>
<OrderNo>77895</OrderNo>
</order>
<order>
<Name>Pettersen</Name>
<P_Id>3</P_Id>
<OrderNo>44678</OrderNo>
</order>
</orders>
用嵌套的for-each来做这件事似乎是非常错误的:它似乎是递归的候选。但我很难理解这个问题的本质。x1 x2等是否出现(0或1)次?在这种情况下,问题肯定是微不足道的,您不需要for-each来迭代单例。
如果您想避免冗长的递归条件代码,那么是时候使用XSLT 2.0了。