XSL for-each和LEFT JOIN

时间:2013-04-09 08:43:04

标签: xml xslt join

我需要一些XSLT语句,它给我一些像“LEFT JOIN”的东西:如果所选节点确实存在,则返回所有这些节点,否则只循环一次。

这与xsl:for-each循环不同,因为当没有这样的节点时,for-each循环返回ZERO行。

这是一个实际的例子。

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行(就像一个FULL JOIN),而我希望看到所有其他值(如LEFT或RIGHT JOIN)。

我可以结合使用xsl:choose,xls:when,xsl:foreach和xsl:否则,但这很长。

我尝试构建自己的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>

2 个答案:

答案 0 :(得分:1)

即使我完全不明白你的问题,我也会尝试回答。 我的理解是你正在锁定像sql left join这样的东西。 (例如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>

答案 1 :(得分:0)

使用嵌套for-each执行此操作似乎非常错误:它似乎是递归的候选者。但我很难理解问题的确切性质。 x1,x2等每次出现(0或1)次?在这种情况下,问题肯定是微不足道的,你不需要for-each迭代单例。

如果你想避免递归的条件代码的冗长,那么现在是你搬到XSLT 2.0的时候了。