Rise_against
Rise_against

Reputation: 1060

XSLT copy starting from certain node

I want to move a part of an XML file into another node using XSLT. I only want to move everything starting a certain node. It will be best to explain through an example.

This would be the input XML (it's just a sample)

<Messages>
  <Message>
    <Header>
        <Name>Message Content 1</Name>
    </Header>
    <Info1>
        <Description>blabla</Description>
    </Info1>
    <Info2>
        <Name>Test</Name>
        <Description>blabla</Description>
    </Info2>
    ... Possible more nodes (with random names)
    <Header>
        <Name>Message Content Summary</Name>
    </Header>
    <Info1>
        <Total>blablabla</Total>
    </Info1>
    ... Possible more nodes (with random names)
  </Message>
</Messages>

I would like to get the following output:

<Messages>
  <Message>
    <Header>
        <Name>Message Content 1</Name>
    </Header>
    <Info1>
        <Description>blabla</Description>
    </Info1>
    <Info2>
        <Name>Test</Name>
        <Description>blabla</Description>
    </Info2>
    ... Possible more nodes (with random names)
  </Message>
  <MessageSummary>
    <Header>
        <Name>Message Content Summary</Name>
    </Header>
    <Info1>
        <Total>blablabla</Total>
    </Info1>
    ... Possible more nodes (with random names)
  </MessageSummary>
</Messages>

So I want to move everything starting from the last occurance of the node "Header" into a seperate node "MessageSummary".

Is there any way I can accomplish this using XSLT 1.0? Any help would be greatly appreciated.

Thanks.

Upvotes: 1

Views: 1567

Answers (1)

Ian Roberts
Ian Roberts

Reputation: 122364

So when you're at the context of the input Message element, you want to put any element that has at least one Header after it into the output Message and everything else inside MessageSummary. You can distinguish these two cases as *[following-sibling::Header] and *[not(following-sibling::Header)], so if we start from a standard identity transformation we end up with

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

  <!-- identity template - copies everything as-is unless overridden -->
  <xsl:template match="@*|node()">
    <xsl:copy><xsl:apply-templates select="@*|node()" /></xsl:copy>
  </xsl:template>

  <xsl:template match="Message">
    <xsl:copy>
      <xsl:apply-templates select="*[following-sibling::Header]" />
    </xsl:copy>
    <MessageSummary>
      <xsl:apply-templates select="*[not(following-sibling::Header)]" />
    </MessageSummary>
  </xsl:template>
</xsl:stylesheet>

If you have other elements named Message in the "random names" section you may wish to make this template more specific, e.g.

  <xsl:template match="Messages/Message">

so it only catches the elements you really want.

Upvotes: 2

Related Questions