Bastian Schuetz
Bastian Schuetz

Reputation: 1

XSL - How to ouput nested text-elements at the correct position

I am trying to create an xsl-stylesheet that outputs my xml-contents in the correct order. Here is an example: XML:

...<p>This is<mark> a nested <b>text</b></mark></p>...

XSL:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<html> 
<body>
  <h2>
    <xsl:value-of select="html/head/title"/>
  </h2>
  <div style="border:1px solid black;margin:30px;padding:30px;box-sizing:border-box;">
    <xsl:for-each select="html/body/div[@class='toc']/table/tr/td/a">
      <p><a>
        <xsl:attribute name="href" namespace="uri">
          <xsl:value-of select="current()/@href"/>
        </xsl:attribute> 
        <xsl:value-of select="current()"/>
      </a></p>
    </xsl:for-each>
  </div>
  <xsl:for-each select="html/body/div[@class='chapter']">
    <div style="border:1px solid black;margin:30px;padding:30px;box-sizing:border-box;">
        <xsl:attribute name="id" namespace="uri"><xsl:value-of select ="current()/@id"/></xsl:attribute> 
        <p><xsl:value-of select ="current()/@id"/></p>
        <xsl:call-template name="rec">
          <xsl:with-param name="parents" select="current()"/>
        </xsl:call-template>
        
    </div>
  </xsl:for-each>
</body>
</html>
</xsl:template>

<xsl:template name="rec">
<xsl:param name="parents"></xsl:param>
  <xsl:for-each select="$parents/*">
    <xsl:if test="name() = 'img'">
      <img class="{@class}" src="{@src}" style="max-width:100%;"/>
    </xsl:if>
    <xsl:if test="name() != 'img'">
      <xsl:element name="{local-name()}">
        <xsl:if test="name() != 'figure'">
          <xsl:value-of select ="current()"/>
        </xsl:if>
        <xsl:call-template name="rec">
          <xsl:with-param name="parents" select="current()"/>
        </xsl:call-template>
      </xsl:element>
    </xsl:if>
  </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

This outputs:

This is a nested text
a nested text
text

What I am trying to get:

<p>This is<mark> a nested <b>text</b></mark></p>

I have tried just to include a CSS-Stylesheet (which would get rid of this particular problem), however this does not seem to work with images (e.g.), which won´t be displayed but will occure inside most documents. The XSL-Stylesheet is supposed to be working with multiple documents (I wrote an exporter, that creates xml-files, that roughly follow the same syntax). The important part should only be the recursive function inside <xsl:template name="rec">.

Help would be greatly appreciated. Thanks!

Upvotes: 0

Views: 186

Answers (1)

Martin Honnen
Martin Honnen

Reputation: 167696

Basic push style, structure and order preserving processing usually relies on the identity transformation template plus custom templates for each node you need to transform e.g.

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="img">
      <img class="{@class}" src="{@src}" style="max-width:100%;"/>
  </xsl:template>

The duplicated text in your wrong output is created by the repeated use of xsl:value-of in the recursive, named template. If you treat text as nodes and let any copying be handled through adequate templates, like the identity transformation template, you don't output text values several times.

Upvotes: 1

Related Questions