Dono
Dono

Reputation: 53

Creating new node based on detection of child node

The feedback from my previous post worked a charm but I have encountered the following with some of the documents on our system. The output is as below.

<par def='1'>
   <run>This is start of line one para one </run>
   <run>text hotspot 1</run>
   <run> remainder of line one<break/></run>

   <run>This is line 2 </run>
   <run>another hotspot </run>
   <run>remainder of line 2 <break/></run>
 </par>

Is it possible to generate the following output using XSLT?

<document>
   <para>This is start of line one para one text hotspot 1 remainder of line one</para>

   <para>This is line 2 another hotspot remainder of line 2</para>
</document>

ie, the <break/> node indicates the end of a sentence but a sentence may run over several <run> nodes.

In case anyone is wondering, the source data is generated from Lotus Notes in it's DXL schema format.

I have been using a 3rd party tool to generate my XSLT to date, I'm happy to provide the code but it's not very clean.

Thank you again in advance, becoming a huge fan of this forum.

Dono

Upvotes: 2

Views: 120

Answers (3)

Enthusiastic
Enthusiastic

Reputation: 549

Though isn't a preferable method it works your way..

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

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

  <xsl:template match="par">
    <document>
      <para>
        <xsl:apply-templates select="node()"/>
      </para>
    </document>
  </xsl:template>

  <xsl:template match="run">
    <xsl:value-of select="."/>
    <xsl:apply-templates select="break"/>
  </xsl:template>

  <xsl:template match="break">
    <xsl:value-of select="'&#60;/para&#62;'" disable-output-escaping="yes"/>
    <xsl:value-of select="'&#60;para&#62;'" disable-output-escaping="yes"/>
  </xsl:template>
</xsl:stylesheet>

Upvotes: 1

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243469

This transformation:

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

 <xsl:key name="kPreceding" match="run"
      use="generate-id((following::break|descendant::break)[1])"/>

 <xsl:template match="par">
     <document>
      <xsl:apply-templates select="run/break"/>
     </document>
 </xsl:template>

 <xsl:template match="break">
  <para><xsl:apply-templates select="key('kPreceding', generate-id())/text()"/></para>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<par def='1'>
    <run>This is start of line one para one </run>
    <run>text hotspot 1</run>
    <run> remainder of line one<break/></run>

    <run>This is line 2 </run>
    <run>another hotspot </run>
    <run>remainder of line 2<break/></run>
</par>

produces the wanted, correct result:

<document>
   <para>This is start of line one para one text hotspot 1 remainder of line one</para>
   <para>This is line 2 another hotspot remainder of line 2</para>
</document>

Explanation:

This is a typical XSLT 1.0 positional grouping solution. We use a key to express the relationship between a break element and all the run elements which it identifies as a group.

Upvotes: 0

Thomas W
Thomas W

Reputation: 15371

What about this? It only creates new <para> elements for the first <run> in a <par> and if the immediately preceding <par> has a <break> in it.

<xsl:template match="par">
  <xsl:for-each select="run[preceding-sibling::run[1]/break or not(preceding-sibling::run)]">
    <para>
      <xsl:apply-templates select="."/>
    </para>
  </xsl:for-each>
</xsl:template>

<xsl:template match="run">
  <xsl:value-of select="."/>
  <xsl:if test="not(break)">
    <xsl:apply-templates select="following-sibling::run[1]"/>
  </xsl:if>
</xsl:template>

Upvotes: 1

Related Questions