开发者

In XSLT, how can I insert matching tags that apply to different templates

I'm very new to XSLT and just haven't been able to find a solution yet to my problem. I have an xml file that looks like (and I can't change the way this xml looks, realizing it's a bit odd):

<account>
    <name>accountA</name>
</account>
<period>
    <type>priormonth</type>
</period>
<period>
    <type>currentmonth</type>
</period>
<account>
    <name>accountB</name>
</account>
<period>
    <type>priormonth</type>
</period>
<period>
    <type>currentmonth</type>
</period>

The xml file can have a variable number of account/period/period datasets but they are always in that order.

I've got an xsl file which looks like:

<xsl:template match="/">
    <xsl:apply-templates/>
</xsl:template>

<xsl:template match="account">
    <name> <xsl:value-of select="name"/> </name>
</xsl:template>

&开发者_StackOverflow社区lt;xsl:template match="period">
    <type> <xsl:value-of select="type"/> </type>
</xsl:template>

The above works great because it deals with the multiple account/period/period occurrences and my output comes out like:

<name>accountA</name>
<type>priormonth</type>
<type>currentmonth</type>
<name>accountB</name>
<type>priormonth</type>
<type>currentmonth</type>

However, I'm wanting to have some additional tags inserted so that the output actually looks like:

<account>
    <name>accountA</name>
    <period>
        <type>priormonth</type>
        <type>currentmonth</type>
    </period>
</account>
<account>
    <name>accountB</name>
    <period>
        <type>priormonth</type>
        <type>currentmonth</type>
    </period>
</account>

Is there a way of doing this? Apologies if my terminology is not quite right. Thanks


Try this:

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

  <xsl:template match="account">
    <xsl:copy>
      <xsl:apply-templates />
      <period>
        <xsl:apply-templates select="following-sibling::period[generate-id(preceding-sibling::account[1]) = generate-id(current())]/*" />
      </period>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="period" />

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

The select statement is a bit convoluted, but what it basically does it select all period siblings following each account element, where the first account element that precedes it is the current one.


The following XSLT assumes that the xml source is exactly as presented in your question (apart the missing root which makes the source document not well formed). In this case you do not need identity transform. Moreover, if really your account needs only the first next two period you can use a simpler XPath.


XSLT 1.0 tested under Saxon-HE 9.2.1.1J

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

    <xsl:template match="/root">
        <xsl:copy>
            <xsl:apply-templates select="account"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="account">
        <xsl:copy>
            <xsl:copy-of select="name" />
            <period>
                <xsl:copy-of select="following-sibling::period[position()&lt;=2]/type" />
            </period>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="period" />

</xsl:stylesheet>

Applied on this input:

<?xml version="1.0" encoding="ISO-8859-1"?>
<root>
    <account>
        <name>accountA</name>
    </account>
    <period>
        <type>march</type>
    </period>
    <period>
        <type>currentmonth</type>
    </period>
    <account>
        <name>accountB</name>
    </account>
    <period>
        <type>priormonth</type>
    </period>
    <period>
        <type>currentmonth</type>
    </period>
</root>

Gives:

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <account>
      <name>accountA</name>
      <period>
         <type>march</type>
         <type>currentmonth</type>
      </period>
   </account>
   <account>
      <name>accountB</name>
      <period>
         <type>priormonth</type>
         <type>currentmonth</type>
      </period>
   </account>
</root>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜