xslt如何选择当前节点或前一个兄弟节点而不是两者

时间:2015-09-22 17:45:24

标签: xml xslt xpath

所以我的xml看起来像这样

<Data>
    <History>
        <z r="1" c1="BILL" c2="123" c3="02/20/2006" c4="100.00" c5=".00" c6=".00" c7="100.00" c8=".00" c9=".00" c10=".00" lcdesc="BILL"/>
        <z r="2" c1="PAYCODE" c2="456" c3="03/16/2006" c4="-100.00" c5=".00" c6=".00" c7="-100.00" c8=".00" c9=".00" c10=".00" lcdesc="PAYMENT"/>
        <z r="3" c1="Total" c2="123" c3="02/20/2006" c4=".00" c5=".00" c6=".00" c7=".00" c8=".00" c9=".00" c10=".00" lcdesc="Total"/>
        <z r="4" lcdesc=""/>
        <z r="5" c1="BILL" c2="124" c3="03/27/2006" c4="13000.00" c5="400.00" c6=".00" c7="13400.00" c8=".00" c9=".00" c10=".00" lcdesc="BILL"/>
        <z r="6" c1="PAYCODE" c2="457" c3="04/13/2006" c4="-13000.00" c5="-400.00" c6=".00" c7="-13400.00" c8=".00" c9=".00" c10=".00" lcdesc="PAYMENT"/>
        <z r="7" c1="Total" c2="124" c3="03/27/2006" c4=".00" c5=".00" c6=".00" c7=".00" c8=".00" c9=".00" c10=".00" lcdesc="Total"/>
        <z r="8" lcdesc=""/>
        <z r="9" c1="BILL" c2="125" c3="04/28/2006" c4="1000.00" c5=".00" c6=".00" c7="1000.00" c8=".00" c9=".00" c10=".00" lcdesc="BILL"/>
        <z r="10" c1="PAYCODE" c2="458" c3="05/10/2006" c4="-1000.00" c5=".00" c6=".00" c7="-1000.00" c8=".00" c9=".00" c10=".00" lcdesc="PAYMENT"/>
        <z r="11" c1="Total" c2="125" c3="04/28/2006" c4=".00" c5=".00" c6=".00" c7=".00" c8=".00" c9=".00" c10=".00" lcdesc="Total"/>
        <z r="12" lcdesc=""/>
   </History>
</Data>

我想从c1 =&#34; BILL&#34;中提取一些信息。后续行上的z节点。我想获得发票号(c2)和发票日期(c3)。所以我在我的模板中想到,如果c1 =&#39; BILL&#39;那么我会声明一个变量并对当前节点进行选择。或者前面的兄弟,其中c1 =&#39; BILL&#39;。虽然它适用于第一个节点及其后续行,但在第二个帐单行上它会放置先前的帐单行信息。所以有些事情是对的。你能帮忙吗?

这是我的非工作xslt:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:rs="urn:schemas-microsoft-com:rowset" xmlns:z="#RowsetSchema">
<xsl:output method="xml" omit-xml-declaration="yes"/>
    <xsl:template match="*|/">
        <xsl:apply-templates/>
    </xsl:template>
    <xsl:template match="text()|@*">
        <xsl:value-of select="."/>
    </xsl:template>
    <xsl:output indent="no"/>
        <xsl:template match="/">
<History>
                <xsl:apply-templates select="//History/z"/>
</History>
    </xsl:template>


    <xsl:template match="/PageData/History/z">
<xsl:variable name="invnode" select="current()[@c1 = 'BILL'] | preceding-sibling::z[@c1 = 'BILL'][1]" />
        <z>
            <xsl:for-each select="@*">
                <!--get the attribute name-->
                <xsl:variable name="nodename">
                    <xsl:value-of select="name()"/>
                </xsl:variable>
                <xsl:attribute name="{$nodename}"><xsl:value-of select="."/></xsl:attribute>
            </xsl:for-each>
                        <xsl:attribute name="invoice"><xsl:value-of select="$invnode/@c2" /></xsl:attribute>
                        <xsl:if test="string-length($invnode/@c3) >= 10">
                                <xsl:attribute name="invdate1"><xsl:value-of select="concat(substring($invnode/@c3, 7), substring($invnode/@c3, 1, 2), substring($invnode/@c3, 4, 2))" /></xsl:attribute>
                                <xsl:attribute name="invdate2"><xsl:value-of select="concat(substring($invnode/@c3, 7), '-', substring($invnode/@c3, 1, 2), '-', substring($invnode/@c3, 4, 2))" /></xsl:attribute>
                        </xsl:if>
        </z>
    </xsl:template>

</xsl:stylesheet>

我想要的输出如下:

<Data>
   <History>
      <z r="1" c1="BILL" c2="123" c3="02/20/2006" c4="100.00" c5=".00" c6=".00" c7="100.00" c8=".00" c9=".00" c10=".00" lcdesc="BILL" invoice="123" invdate1="20060220" invdate2="2006-02-20" />
      <z r="2" c1="PAYCODE" c2="456" c3="03/16/2006" c4="-100.00" c5=".00" c6=".00" c7="-100.00" c8=".00" c9=".00" c10=".00" lcdesc="PAYMENT"  invoice="123" invdate1="20060220" invdate2="2006-02-20" />
      <z r="3" c1="Total" c2="123" c3="02/20/2006" c4=".00" c5=".00" c6=".00" c7=".00" c8=".00" c9=".00" c10=".00" lcdesc="Total" invoice="123" invdate1="20060220" invdate2="2006-02-20" />
      <z r="4" lcdesc="" invoice="123" invdate1="20060220" invdate2="2006-02-20" />
      <z r="5" c1="BILL" c2="124" c3="03/27/2006" c4="13000.00" c5="400.00" c6=".00" c7="13400.00" c8=".00" c9=".00" c10=".00" lcdesc="BILL"  invoice="124" invdate1="20060327" invdate2="2006-03-27" />
      <z r="6" c1="PAYCODE" c2="457" c3="04/13/2006" c4="-13000.00" c5="-400.00" c6=".00" c7="-13400.00" c8=".00" c9=".00" c10=".00" lcdesc="PAYMENT" invoice="124" invdate1="20060327" invdate2="2006-03-27" />
      <z r="7" c1="Total" c2="124" c3="03/27/2006" c4=".00" c5=".00" c6=".00" c7=".00" c8=".00" c9=".00" c10=".00" lcdesc="Total" invoice="124" invdate1="20060327" invdate2="2006-03-27" />
      <z r="8" lcdesc="" invoice="124" invdate1="20060327" invdate2="2006-03-27" />
      <z r="9" c1="BILL" c2="125" c3="04/28/2006" c4="1000.00" c5=".00" c6=".00" c7="1000.00" c8=".00" c9=".00" c10=".00" lcdesc="BILL" invoice="125" invdate1="20060428" invdate2="2006-04-28" />
      <z r="10" c1="PAYCODE" c2="458" c3="05/10/2006" c4="-1000.00" c5=".00" c6=".00" c7="-1000.00" c8=".00" c9=".00" c10=".00" lcdesc="PAYMENT" invoice="125" invdate1="20060428" invdate2="2006-04-28" />
      <z r="11" c1="Total" c2="125" c3="04/28/2006" c4=".00" c5=".00" c6=".00" c7=".00" c8=".00" c9=".00" c10=".00" lcdesc="Total" invoice="125" invdate1="20060428" invdate2="2006-04-28" />
      <z r="12" lcdesc="" invoice="125" invdate1="20060428" invdate2="2006-04-28" />
   </History>
</Data>

2 个答案:

答案 0 :(得分:2)

以这种方式试试吗?

XSLT 1.0

<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="*"/>

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

<xsl:template match="z">
    <xsl:variable name="invnode" select="current()[@c1='BILL'] | preceding-sibling::z[@c1 = 'BILL'][1][not(current()/@c1='BILL')]" />
    <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:attribute name="invoice">
            <xsl:value-of select="$invnode/@c2" />
        </xsl:attribute>
        <xsl:attribute name="invdate1">
            <xsl:value-of select="concat(substring($invnode/@c3, 7), substring($invnode/@c3, 1, 2), substring($invnode/@c3, 4, 2))" />
        </xsl:attribute>
        <xsl:attribute name="invdate2">
            <xsl:value-of select="concat(substring($invnode/@c3, 7), '-', substring($invnode/@c3, 1, 2), '-', substring($invnode/@c3, 4, 2))" />
        </xsl:attribute>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

<强>解释

这个表达式:

current()[@c1='BILL']

选择当前节点,前提是当前节点是发票;否则它返回一个空节点集。

这个表达式:

preceding-sibling::z[@c1 = 'BILL'][1][not(current()/@c1='BILL')]"

返回最近的作为发票的在前兄弟,前提是当前节点不是发票;否则它返回一个空节点集。

因此,在这两个表达式的并集中,其中一个子集将始终为空:

  • 如果当前节点是发票,则左侧返回当前节点,右侧为空。

  • 如果当前节点不是发票,则左侧为空,右侧返回最近的前一个兄弟,即发票。

因此,union只会包含一个节点,并且顺序没有意义。

答案 1 :(得分:2)

怎么样......

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

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

<xsl:template match="z">
  <z invoice="{(.|preceding-sibling::z)[@c1='BILL'][last()]/@c2}">
    <xsl:apply-templates select="(.|preceding-sibling::z)[@c1='BILL'][last()]/@c3" mode="inv-date" />
    <xsl:apply-templates select="@*|node()"/>
  </z>
</xsl:template>

<xsl:template match="@c3" mode="inv-date" />

<xsl:template match="@c3[string-length(.) &gt;= 10]" mode="inv-date">
  <xsl:attribute name="invdate1"><xsl:value-of select="concat(substring(., 7),      substring(., 1, 2),      substring(., 4, 2))" /></xsl:attribute>
  <xsl:attribute name="invdate2"><xsl:value-of select="concat(substring(., 7), '-', substring(., 1, 2), '-', substring(., 4, 2))" /></xsl:attribute>
</xsl:template>

</xsl:stylesheet>