Grief
Grief

Reputation: 2040

XSLT split elements by splitter-element

I've tried to combine solutions to the similar questions asked here but all of them work in very specific cases. My case is the following: I have an arbitrary xml document which contains some tags, let's say <separator/>. What I want is to split parent elements of these tags like that:

INPUT:

<some_tag some_attr="some_value">
    some text
    <some_other_tag>some another text</some_other_tag>
    <separator/>
    some other content
</some_tag>

OUTPUT:

<some_tag some_attr="some_value">
    some text
    <some_other_tag>some another text</some_other_tag>
</some_tag>
<separator/>
<some_tag some_attr="some_value">
    some other content
</some_tag>

Also, I am limited to XSLT 1.0 since Xalan is used in the project

Upvotes: 1

Views: 570

Answers (1)

Martin Honnen
Martin Honnen

Reputation: 167571

Either use sibling recursion or use a key to find the nodes "belonging" to a separator. Additional care is needed to copy stuff following the last separator:

<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

  <xsl:output method="xml" indent="yes"/>

  <xsl:key name="sep" match="*[separator]/node()[not(self::separator)]" use="generate-id(following-sibling::separator[1])"/>

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

  <xsl:template match="*[separator]">
      <xsl:apply-templates select="separator" mode="split"/>
      <xsl:if test="separator[last()]/following-sibling::node()">
          <xsl:copy>
              <xsl:apply-templates select="@* | separator[last()]/following-sibling::node()"/>
          </xsl:copy>
      </xsl:if>
  </xsl:template>

  <xsl:template match="separator" mode="split">
      <xsl:apply-templates select=".." mode="split">
          <xsl:with-param name="separator" select="."/>
      </xsl:apply-templates>
      <xsl:copy-of select="."/>
  </xsl:template>

  <xsl:template match="*" mode="split">
      <xsl:param name="separator"/>
      <xsl:copy>
          <xsl:apply-templates select="@* | key('sep', generate-id($separator))"/>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/pPqsHTc

Note that if you use Xalan Java then in the Java world this is much easier with Saxon 9 and XSLT 2/3's <xsl:for-each-group select="node()" group-adjacent="boolean(self::separator)"> or group-starting-with="separator".

Upvotes: 3

Related Questions