Rendering nodes in different order

Here should go questions about transforming XML with XSLT and FOP.
Holmanit
Posts: 2
Joined: Thu Dec 03, 2020 7:48 pm

Rendering nodes in different order

Post by Holmanit »

Hello.

That's my source XML

Code: Select all

<root>
    <status> status </status> 
        <content>
        <Description>
            <title>Title </title>
            <some_node>some node </some_node>
           </Description>
    </content>
</root>
Sometimes I need title to be displayed before status. That's my code using Oxygen 21.1

Code: Select all

<xsl:stylesheet>
    <xsl:param name="TitleOnTop" select="'true'"/>
    <xsl:template match="root">
    <xsl:if test="$TitleOnTop='true'">
        <xsl:apply-templates select="//title[1]"></xsl:apply-templates>
    </xsl:if>
     <xsl:choose>
        <xsl:when test="$TitleOnTop='true'">
            <xsl:apply-templates select="*[not(self::title)]"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:apply-templates/>
        </xsl:otherwise>
    </xsl:choose>
    </xsl:template>
    
    <xsl:template match="title">
        <xsl:apply-templates/>
    </xsl:template>
    
   
</xsl:stylesheet>:
The problem is that <title> is rendered twice both before and after status. How do I fix that?

Output I get :
Title status
Title
some node

Output I want:
Title status
some node
Radu
Posts: 9018
Joined: Fri Jul 09, 2004 5:18 pm

Re: Rendering nodes in different order

Post by Radu »

Hi,

I re-wrote the stylesheet a bit and added some comments inside it:

Code: Select all

<xsl:stylesheet  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="xs"
  version="2.0">
  <xsl:param name="TitleOnTop" select="'true'"/>
  
  <!--This is the default processing which copies any element exactly as it is-->
  <xsl:template match="node() | @*">
    <xsl:copy>
      <xsl:apply-templates select="node() | @*"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="root">
    <!-- Copy the element tag to the output -->
    <xsl:copy>
      <!-- Copy all attributes as they are -->
      <xsl:apply-templates select="@*"/>
      <!-- Copy the title if necessary -->
      <xsl:if test="$TitleOnTop='true'">
        <xsl:apply-templates select="//title[1]"/>
      </xsl:if>
      <!-- Continue further on all descendents -->
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>
  
  <!-- Copy the Description element to the output, except its title if the param is enabled -->
  <xsl:template match="Description">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:choose>
        <xsl:when test="$TitleOnTop='true'">
          <xsl:apply-templates select="*[not(self::title)]"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:apply-templates/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:copy>
  </xsl:template>
  
</xsl:stylesheet>
Regards,
Radu
Radu Coravu
<oXygen/> XML Editor
http://www.oxygenxml.com
Holmanit
Posts: 2
Joined: Thu Dec 03, 2020 7:48 pm

Re: Rendering nodes in different order

Post by Holmanit »

Thank you.
These gives me
<root><title>Title </title>
<status> status </status>
<content>
<Description><some_node>some node </some_node></Description>
</content>
</root>
in output.

And I need only
Title
status
some node
I.E. just content of nodes.

2. How can I do it in case I don't know depth of <title>?
Radu
Posts: 9018
Joined: Fri Jul 09, 2004 5:18 pm

Re: Rendering nodes in different order

Post by Radu »

Hi,

If you only want to output only plain text from the XML, probably the XSLT would be something like this:

Code: Select all

<xsl:stylesheet  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">
    <xsl:output method="text"/>
    <xsl:param name="TitleOnTop" select="'true'"/>
    <!--This is the default processing which copies any element exactly as it is-->
    <xsl:template match="node() | @*">
        <xsl:copy>
            <xsl:apply-templates select="node() | @*"/>
        </xsl:copy>
    </xsl:template>
    
    <!-- Only output the text -->
    <xsl:template match="*">
        <xsl:apply-templates select="node() | @*"/>
    </xsl:template>
    
    <xsl:template match="root">
        <!-- Copy the title if necessary -->
        <xsl:if test="$TitleOnTop='true'">
            <xsl:apply-templates select="//title[1]"/>
        </xsl:if>
        <!-- Continue further on all descendents -->
        <xsl:apply-templates/>
    </xsl:template>
    
    <!-- Copy the Description element to the output, except its title if the param is enabled -->
    <xsl:template match="Description">
        <xsl:choose>
            <xsl:when test="$TitleOnTop='true'">
                <xsl:apply-templates select="*[not(self::title)]"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:apply-templates/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    
</xsl:stylesheet>

About this question:
How can I do it in case I don't know depth of <title>?
Instead of:

Code: Select all

 <xsl:template match="Description">
you can match any element which contains a title like:

Code: Select all

  <xsl:template match="*[title]">
Regards,
Radu
Radu Coravu
<oXygen/> XML Editor
http://www.oxygenxml.com
Post Reply