Home > Software engineering >  Merge Two XSL Files
Merge Two XSL Files

Time:08-24

I've inherited some XSL that I'm struggling with.

Source XML:

<as:article xmlns:as="http://www.example.com/schemas/articleschema">
    <as:parastyles>
        <as:parastyle name="Body ragged" uid="298" />
        <as:parastyle name="Head 1col w rule" uid="304" />
        <as:parastyle name="Headline Light" uid="312" />
    </as:parastyles>
    <as:charstyles>
        <as:charstyle name="[None]" uid="112" />
        <as:charstyle name="Character Style 1" uid="289" />
    </as:charstyles>
    <as:story name="body">
        <as:runs>
            <as:run p="298" c="112">
                <as:eol />Lorem ipsum dolor sit amet, consectetur adipiscing elit. <as:eol />(Aug. 11) ut eros ut elit sollicitudin posuere <as:eol />Aenean maximus sed est vel rhoncus. Aenean sagittis scelerisque erat eu lacinia. Vivamus dignissim sapien ac magna egestas ultricies. Nullam viverra pulvinar diam a hendrerit. Vivamus blandit arcu eget nisl hendrerit, ac bibendum ex suscipit. In id imperdiet velit. In suscipit sodales dapibus. Nullam quis tellus ut ante fringilla pulvinar. Sed congue purus et velit tincidunt, et viverra leo vestibulum.&lt;2028&gt;<as:eol />&lt;2028&gt;<as:eol />Vestibulum consequat augue ac euismod sagittis. Pellentesque ex odio, iaculis laoreet tortor eu, maximus aliquam turpis. Quisque placerat, tortor id sodales venenatis, arcu tellus <as:eol />(commodo felis, sed finibus metus justo eget turpis. Vivamus mattis lorem sit amet porta sagittis. Pellentesque tristique nibh ex, in dapibus urna consequat at. Aenean tellus tellus, venenatis ut sagittis vel, porttitor ut neque. Vivamus at dui sed lectus <as:eol />(ultricies elementum. Aliquam libero quam, <as:eol />(mollis eget eleifend vel, suscipit ac libero. Praesent sit amet libero finibus, varius eros vitae, vulputate ipsum. Morbi a purus orci. Nunc pulvinar fringilla eros a pharetra. Aenean in pellentesque nisi, et pretium diam.&lt;2029&gt;</as:run>
            <as:run p="298" c="112" b="true">
                <as:eol />John Smith&lt;2029&gt;</as:run>
            <as:run p="298" c="112" i="true">Townville</as:run>
            <as:run p="298" c="112">
                <as:eol />&lt;2029&gt;</as:run>
        </as:runs>
    </as:story>
    <as:story name="head">
        <as:runs>
            <as:run p="312" c="289">Lorem Ipsum</as:run>
        </as:runs>
    </as:story>
    <as:story name="column">
        <as:runs>
            <as:run p="304" c="112">LETTER</as:run>
        </as:runs>
    </as:story>
</as:article>

Inherited XSL:

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:as="http://www.example.com/schemas/articleschema">

  <xsl:output method="text"/>

  <xsl:strip-space elements="*"/>

  <xsl:template match="/">
    <xsl:apply-templates select="//as:story[@name='body']"/>
  </xsl:template>

  <xsl:template match="as:run[@i]">
    <xsl:text>&lt;em&gt;</xsl:text><xsl:value-of select="."/><xsl:text>&lt;/em&gt;</xsl:text>
  </xsl:template>

  <xsl:template match="as:run[@b]">
    <xsl:text> &lt;strong&gt;</xsl:text><xsl:value-of select="."/><xsl:text>&lt;/strong&gt; </xsl:text>
  </xsl:template>

  <xsl:template match="as:run[@p=/article/parastyles/parastyle[@name='Head babies-recipe']/@uid]">
    <xsl:text> &lt;strong&gt;</xsl:text><xsl:value-of select="."/><xsl:text>&lt;/strong&gt; </xsl:text>
  </xsl:template>

  <xsl:template match="as:run[@p=/article/parastyles/parastyle[@name='2head']/@uid]">
    <xsl:text> &lt;strong&gt;</xsl:text><xsl:value-of select="."/><xsl:text>&lt;/strong&gt; </xsl:text>
  </xsl:template>

  <xsl:template match="as:run[@p=/article/parastyles/parastyle[@name='Head subhead14']/@uid]">
    <xsl:text> &lt;strong&gt;</xsl:text><xsl:value-of select="."/><xsl:text>&lt;/strong&gt; </xsl:text>
  </xsl:template>

  <xsl:template match="as:eol[@hyphenated]">
    <xsl:text></xsl:text>
  </xsl:template>

  <xsl:template match="as:eol">
    <xsl:text></xsl:text>
  </xsl:template>

  <xsl:variable name="fp2029">
      <xsl:call-template name="string-replace-all-2029">
          <xsl:with-param name="text2029" select="//as:story[@name='body']" />
          <xsl:with-param name="replace2029" select="'&lt;2029&gt;'" />
          <xsl:with-param name="by2029" select="'&#xa;'" />
      </xsl:call-template>
  </xsl:variable>

  <xsl:template match="/">
          <xsl:value-of select="//as:story[@name='body']"/>
  </xsl:template>

  <xsl:template name="string-replace-all-2029">
      <xsl:param name="text2029" />
      <xsl:param name="replace2029" />
      <xsl:param name="by2029" />
      <xsl:choose>
          <xsl:when test="contains($text2029, $replace2029)">
              <xsl:value-of select="substring-before($text2029,$replace2029)"/>
              <xsl:value-of select="$by2029" />
              <xsl:call-template name="string-replace-all-2029">
                  <xsl:with-param name="text2029" 
                      select="substring-after($text2029,$replace2029)"/>
                  <xsl:with-param name="replace2029" select="$replace2029"/>
                  <xsl:with-param name="by2029" select="$by2029"/>
              </xsl:call-template>
          </xsl:when>
          <xsl:otherwise>
              <xsl:value-of select="$text2029" />
          </xsl:otherwise>
      </xsl:choose>
  </xsl:template>

  <xsl:variable name="fp2028">
      <xsl:call-template name="string-replace-all-2028">
          <xsl:with-param name="text2028" select="$fp2029" />
          <xsl:with-param name="replace2028" select="'&lt;2028&gt;'" />
          <xsl:with-param name="by2028" select="'&#xa;'" />
      </xsl:call-template>
  </xsl:variable>

  <xsl:template match="/">
          <xsl:value-of select="$fp2028"/>
  </xsl:template>

  <xsl:template name="string-replace-all-2028">
      <xsl:param name="text2028" />
      <xsl:param name="replace2028" />
      <xsl:param name="by2028" />
      <xsl:choose>
          <xsl:when test="contains($text2028, $replace2028)">
              <xsl:value-of select="substring-before($text2028,$replace2028)"/>
              <xsl:value-of select="$by2028" />
              <xsl:call-template name="string-replace-all-2028">
                  <xsl:with-param name="text2028" 
                      select="substring-after($text2028,$replace2028)"/>
                  <xsl:with-param name="replace2028" select="$replace2028"/>
                  <xsl:with-param name="by2028" select="$by2028"/>
              </xsl:call-template>
          </xsl:when>
          <xsl:otherwise>
              <xsl:value-of select="$text2028" />
          </xsl:otherwise>
      </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

The docs say that XSL is supposed to find the body then:

  1. Replace any designated strong or em sections of text
  2. Find and replace <2029> and <2028> strings with line returns

It would seem that only one or the other of the above functions in the XSL I inherited work at a time. Either I get the strong/em formatting, or the line returns are fixed, depending on how I modify the XSL.

Is there a way to get BOTH applied?

Desired Results:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. (Aug. 11) ut eros ut elit sollicitudin posuere Aenean maximus sed est vel rhoncus. Aenean sagittis scelerisque erat eu lacinia. Vivamus dignissim sapien ac magna egestas ultricies. Nullam viverra pulvinar diam a hendrerit. Vivamus blandit arcu eget nisl hendrerit, ac bibendum ex suscipit. In id imperdiet velit. In suscipit sodales dapibus. Nullam quis tellus ut ante fringilla pulvinar. Sed congue purus et velit tincidunt, et viverra leo vestibulum.

Vestibulum consequat augue ac euismod sagittis. Pellentesque ex odio, iaculis laoreet tortor eu, maximus aliquam turpis. Quisque placerat, tortor id sodales venenatis, arcu tellus (commodo felis, sed finibus metus justo eget turpis. Vivamus mattis lorem sit amet porta sagittis. Pellentesque tristique nibh ex, in dapibus urna consequat at. Aenean tellus tellus, venenatis ut sagittis vel, porttitor ut neque. Vivamus at dui sed lectus (ultricies elementum. Aliquam libero quam, (mollis eget eleifend vel, suscipit ac libero. Praesent sit amet libero finibus, varius eros vitae, vulputate ipsum. Morbi a purus orci. Nunc pulvinar fringilla eros a pharetra. Aenean in pellentesque nisi, et pretium diam.
<strong>John Smith</strong>
<em>Townville</em>

CodePudding user response:

I think if you have two separate transformations to perform on the same elements, the best way is often to run a pipeline of two transformations controlled by separate stylesheets.

It's sometimes more convenient to do the two transformations within a single stylesheet using something like

<xsl:variable name="output-of-phase-1">
  <xsl:apply-templates mode="phase-1"/>
</xsl:variable>
<xsl:apply-templates select="$output-of-phase-1" mode="phase-2"/>

CodePudding user response:

There are so many problems in this "inherited" stylesheet that I don't know where to start. Just a couple of notes on things that I see.

There are three different templates for matching the root document node.

First:

  <xsl:template match="/">
    <xsl:apply-templates select="//as:story[@name='body']"/>
  </xsl:template>

Then:

  <xsl:template match="/">
          <xsl:value-of select="//as:story[@name='body']"/>
  </xsl:template>

Finally:

  <xsl:template match="/">
          <xsl:value-of select="$fp2028"/>
  </xsl:template>

Since these three templates match an identical item (the root document-node) and they have no priorities assigned, only this third template matching "/" will be selected. That's what's giving the current output in this inherited worksheet.

According to your description and to your desired output, you want to also execute at least these two templates:

  <xsl:template match="as:run[@i]">
    <xsl:text>&lt;em&gt;</xsl:text><xsl:value-of select="."/><xsl:text>&lt;/em&gt;</xsl:text>
  </xsl:template>

  <xsl:template match="as:run[@b]">
    <xsl:text> &lt;strong&gt;</xsl:text><xsl:value-of select="."/><xsl:text>&lt;/strong&gt; </xsl:text>
  </xsl:template>

However, your current worksheet will never execute these templates because they are never applied from this third template matching the root document.

I've tried to apply the sound principles that Dr. Kay has described above by performing successive operations from your worksheet on intermediate variables. I've commented out some of the dead code but haven't removed it entirely. I believe that this is closer to your expected output, although not identical.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:as="http://www.example.com/schemas/articleschema">

  <xsl:output method="text"/>

  <xsl:strip-space elements="*"/>

  <xsl:template match="/">
    <xsl:variable name="matchStrongAndEm">
      <xsl:apply-templates select="//as:story[@name='body']"/>
    </xsl:variable>
    <xsl:variable name="replace2028">
      <xsl:call-template name="string-replace-all-2028">
          <xsl:with-param name="text2028" select="$matchStrongAndEm" />
          <xsl:with-param name="replace2028" select="'&lt;2028&gt;'" />
          <xsl:with-param name="by2028" select="'&#xa;'" />
      </xsl:call-template>
    </xsl:variable>
      <xsl:call-template name="string-replace-all-2029">
          <xsl:with-param name="text2029" select="$replace2028" />
          <xsl:with-param name="replace2029" select="'&lt;2029&gt;'" />
          <xsl:with-param name="by2029" select="'&#xa;'" />
      </xsl:call-template>
  </xsl:template>
  
  <xsl:template match="//as:runs">
    <xsl:apply-templates />
  </xsl:template>

  <xsl:template match="as:run[@i]">
    <xsl:text>&lt;em&gt;</xsl:text><xsl:value-of select="."/><xsl:text>&lt;/em&gt;</xsl:text>
  </xsl:template>

  <xsl:template match="as:run[@b]">
    <xsl:text> &lt;strong&gt;</xsl:text><xsl:value-of select="."/><xsl:text>&lt;/strong&gt; </xsl:text>
  </xsl:template>

  <xsl:template match="as:run[@p=/article/parastyles/parastyle[@name='Head babies-recipe']/@uid]">
    <xsl:text> &lt;strong&gt;</xsl:text><xsl:value-of select="."/><xsl:text>&lt;/strong&gt; </xsl:text>
  </xsl:template>

  <xsl:template match="as:run[@p=/article/parastyles/parastyle[@name='2head']/@uid]">
    <xsl:text> &lt;strong&gt;</xsl:text><xsl:value-of select="."/><xsl:text>&lt;/strong&gt; </xsl:text>
  </xsl:template>

  <xsl:template match="as:run[@p=/article/parastyles/parastyle[@name='Head subhead14']/@uid]">
    <xsl:text> &lt;strong&gt;</xsl:text><xsl:value-of select="."/><xsl:text>&lt;/strong&gt; </xsl:text>
  </xsl:template>

  <xsl:template match="as:eol[@hyphenated]">
    <xsl:text></xsl:text>
  </xsl:template>

  <xsl:template match="as:eol">
    <xsl:text></xsl:text>
  </xsl:template>

  <!--

  <xsl:variable name="fp2029">
      <xsl:call-template name="string-replace-all-2029">
          <xsl:with-param name="text2029" select="//as:story[@name='body']" />
          <xsl:with-param name="replace2029" select="'&lt;2029&gt;'" />
          <xsl:with-param name="by2029" select="'&#xa;'" />
      </xsl:call-template>
  </xsl:variable>

  <xsl:template match="/">
          <xsl:value-of select="//as:story[@name='body']"/>
  </xsl:template>
  -->

  <xsl:template name="string-replace-all-2029">
      <xsl:param name="text2029" />
      <xsl:param name="replace2029" />
      <xsl:param name="by2029" />
      <xsl:choose>
          <xsl:when test="contains($text2029, $replace2029)">
              <xsl:value-of select="substring-before($text2029,$replace2029)"/>
              <xsl:value-of select="$by2029" />
              <xsl:call-template name="string-replace-all-2029">
                  <xsl:with-param name="text2029" 
                      select="substring-after($text2029,$replace2029)"/>
                  <xsl:with-param name="replace2029" select="$replace2029"/>
                  <xsl:with-param name="by2029" select="$by2029"/>
              </xsl:call-template>
          </xsl:when>
          <xsl:otherwise>
              <xsl:value-of select="$text2029" />
          </xsl:otherwise>
      </xsl:choose>
  </xsl:template>

  <!--
  
  <xsl:variable name="fp2028">
      <xsl:call-template name="string-replace-all-2028">
          <xsl:with-param name="text2028" select="$fp2029" />
          <xsl:with-param name="replace2028" select="'&lt;2028&gt;'" />
          <xsl:with-param name="by2028" select="'&#xa;'" />
      </xsl:call-template>
  </xsl:variable>


  <xsl:template match="/">
          <xsl:value-of select="$fp2028"/>
  </xsl:template>
  
  -->

  <xsl:template name="string-replace-all-2028">
      <xsl:param name="text2028" />
      <xsl:param name="replace2028" />
      <xsl:param name="by2028" />
      <xsl:choose>
          <xsl:when test="contains($text2028, $replace2028)">
              <xsl:value-of select="substring-before($text2028,$replace2028)"/>
              <xsl:value-of select="$by2028" />
              <xsl:call-template name="string-replace-all-2028">
                  <xsl:with-param name="text2028" 
                      select="substring-after($text2028,$replace2028)"/>
                  <xsl:with-param name="replace2028" select="$replace2028"/>
                  <xsl:with-param name="by2028" select="$by2028"/>
              </xsl:call-template>
          </xsl:when>
          <xsl:otherwise>
              <xsl:value-of select="$text2028" />
          </xsl:otherwise>
      </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

This gives the output:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. (Aug. 11) ut eros ut elit sollicitudin posuere Aenean maximus sed est vel rhoncus. Aenean sagittis scelerisque erat eu lacinia. Vivamus dignissim sapien ac magna egestas ultricies. Nullam viverra pulvinar diam a hendrerit. Vivamus blandit arcu eget nisl hendrerit, ac bibendum ex suscipit. In id imperdiet velit. In suscipit sodales dapibus. Nullam quis tellus ut ante fringilla pulvinar. Sed congue purus et velit tincidunt, et viverra leo vestibulum.

Vestibulum consequat augue ac euismod sagittis. Pellentesque ex odio, iaculis laoreet tortor eu, maximus aliquam turpis. Quisque placerat, tortor id sodales venenatis, arcu tellus (commodo felis, sed finibus metus justo eget turpis. Vivamus mattis lorem sit amet porta sagittis. Pellentesque tristique nibh ex, in dapibus urna consequat at. Aenean tellus tellus, venenatis ut sagittis vel, porttitor ut neque. Vivamus at dui sed lectus (ultricies elementum. Aliquam libero quam, (mollis eget eleifend vel, suscipit ac libero. Praesent sit amet libero finibus, varius eros vitae, vulputate ipsum. Morbi a purus orci. Nunc pulvinar fringilla eros a pharetra. Aenean in pellentesque nisi, et pretium diam.
 <strong>John Smith
</strong> <em>Townville</em>

  • Related