LinuxQuestions.org
Help answer threads with 0 replies.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 01-29-2023, 06:27 AM   #1
Turbocapitalist
LQ Guru
 
Registered: Apr 2005
Distribution: Linux Mint, Devuan, OpenBSD
Posts: 7,489
Blog Entries: 3

Rep: Reputation: 3812Reputation: 3812Reputation: 3812Reputation: 3812Reputation: 3812Reputation: 3812Reputation: 3812Reputation: 3812Reputation: 3812Reputation: 3812Reputation: 3812
XSLT to produce nested lists


I have a question about how to use XSL (Extensible Stylesheet Language) to transform some basic XML into an unordered list while retaining the nesting. Here is a sample script containing the XML and the XSL which I have tried.

Code:
#!/usr/bin/python3

import lxml.etree as et

outline = '''<?xml version="1.0" encoding="UTF-8"?>
<xml>
 <o text="1">
  <o text="1.A" />
  <o text="1.B" />
  <o text="1.C" />
 </o>
 <o text="2">
  <o text="2.A">
   <o text="2.A.1" />
   <o text="2.A.2" />
  </o>
  <o text="2.B">
   <o text="2.B.1" />
   <o text="2.B.2" />
  </o>
  <o text="2.C" />
 </o>
</xml>
'''

xsl = '''
<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
  <xsl:template match="/">
    <ul>
      <xsl:for-each select=".//o">
        <li>
          <xsl:value-of select="@text" />
        </li>
      </xsl:for-each>
    </ul>
  </xsl:template>
</xsl:stylesheet>
'''

dom = et.fromstring(outline.encode())
xslt = et.XML(xsl)
transform = et.XSLT(xslt)
dom2 = transform(dom)
html = et.tostring(dom2,pretty_print=True)
print(f"{html}")
That results in a flat list with a single level:

Code:
<ul>\n  <li>1</li>\n  <li>1.A</li>\n  <li>1.B</li>\n  <li>1.C</li>\n  <li>2</li>\n  <li>2.A</li>\n  <li>2.A.1</li>\n  <li>2.A.2</li>\n  <li>2.B</li>\n  <li>2.B.1</li>\n  <li>2.B.2</li>\n  <li>2.C</li>\n</ul>\n
I've tried a lot of variants but don't fully understand XSL and can't get past a flat list.

What I am hoping to figure out is how to preserve the nested aspects. The expected result would be this instead:

Code:
<ul>\n  <li>1\n    <ul>\n   <li>1.A</li>\n   <li>1.B</li>\n   <li>1.C</li>\n   </ul>\n </li>\n   <li>2\n    <ul>\n   <li>2.A\n    <ul>\n   <li>2.A.1</li>\n   <li>2.A.2</li>\n   </ul>\n </li>\n   <li>2.B\n    <ul>\n   <li>2.B.1</li>\n   <li>2.B.2</li>\n   </ul>\n </li>\n   <li>2.C</li>\n   </ul>\n</li>\n   </ul>\n
How should the XSL look to achieve that?
 
Old 01-29-2023, 09:25 AM   #2
ntubski
Senior Member
 
Registered: Nov 2005
Distribution: Debian, Arch
Posts: 3,793

Rep: Reputation: 2087Reputation: 2087Reputation: 2087Reputation: 2087Reputation: 2087Reputation: 2087Reputation: 2087Reputation: 2087Reputation: 2087Reputation: 2087Reputation: 2087
The ".//o" expression produces a flat list of all o elements; rather than iterating over the list, you want to apply <xsl:template match> recursively:

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

  <xsl:template match="/">
    <ul>
      <xsl:apply-templates/>
    </ul>
  </xsl:template>

  <xsl:template match="o">
    <li>
      <xsl:value-of select="@text" />
      <xsl:apply-templates/>
    </li>
  </xsl:template>
</xsl:stylesheet>

Last edited by ntubski; 01-29-2023 at 09:25 AM. Reason: missed a word
 
2 members found this post helpful.
Old 01-29-2023, 09:32 AM   #3
Turbocapitalist
LQ Guru
 
Registered: Apr 2005
Distribution: Linux Mint, Devuan, OpenBSD
Posts: 7,489

Original Poster
Blog Entries: 3

Rep: Reputation: 3812Reputation: 3812Reputation: 3812Reputation: 3812Reputation: 3812Reputation: 3812Reputation: 3812Reputation: 3812Reputation: 3812Reputation: 3812Reputation: 3812
Thanks, I'll now try experimenting with that. Would there be a way for to include the UL elements too each time a new level is entered? The goal is that the output is valid HTML. I suppose some kind of XPath with first-child or last-child would be too much of a kludge.
 
Old 01-29-2023, 10:09 AM   #4
ntubski
Senior Member
 
Registered: Nov 2005
Distribution: Debian, Arch
Posts: 3,793

Rep: Reputation: 2087Reputation: 2087Reputation: 2087Reputation: 2087Reputation: 2087Reputation: 2087Reputation: 2087Reputation: 2087Reputation: 2087Reputation: 2087Reputation: 2087
Quote:
Originally Posted by Turbocapitalist View Post
Would there be a way for to include the UL elements too each time a new level is entered? The goal is that the output is valid HTML.
Oh, missed that part. I guess every level but the last?
Code:
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

  <xsl:template match="/">
    <ul>
      <xsl:apply-templates/>
    </ul>
  </xsl:template>

  <xsl:template match="o[./o]"> <!-- o elements containing o elements -->
      <ul>
        <li>
          <xsl:value-of select="@text" />
          <xsl:apply-templates/>
        </li>
      </ul>
  </xsl:template>

  <xsl:template match="o">      <!-- other o elements (i.e., lowest level) -->
    <li>
      <xsl:value-of select="@text" />
    </li>
  </xsl:template>
</xsl:stylesheet>
 
2 members found this post helpful.
Old 01-29-2023, 01:57 PM   #5
Turbocapitalist
LQ Guru
 
Registered: Apr 2005
Distribution: Linux Mint, Devuan, OpenBSD
Posts: 7,489

Original Poster
Blog Entries: 3

Rep: Reputation: 3812Reputation: 3812Reputation: 3812Reputation: 3812Reputation: 3812Reputation: 3812Reputation: 3812Reputation: 3812Reputation: 3812Reputation: 3812Reputation: 3812
Thanks! Having two XPath patterns was the right approach. The "o[./o]" was needed to wrap the descendants inside unordered lists:

Code:
xsl = '''                                                                       
<xsl:stylesheet version="1.0"                                                   
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">               
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>        
                                                                                
  <xsl:template match="/">                                                      
    <ul>                                                                        
      <xsl:apply-templates/>                                                    
    </ul>                                                                       
  </xsl:template>                                                               
                                                                                
  <xsl:template match="o[./o]"> <!-- o elements containing o elements -->       
      <li>                                                                      
          <xsl:value-of select="@text" />                                       
        <ul>                                                                    
          <xsl:apply-templates/>                                                
        </ul>                                                                   
      </li>                                                                     
  </xsl:template>                                                               
                                                                                
  <xsl:template match="o">      <!-- other o elements (i.e., lowest level) -->  
    <li>                                                                        
      <xsl:value-of select="@text" />                                           
    </li>                                                                       
  </xsl:template>                                                               
</xsl:stylesheet>                                                               
'''
I'm familiar with XPaths but the use of XSLT is still (hours) new to me. So this helped a lot.
 
  


Reply

Tags
nesting, unordered list, xslt


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Generate XSLT with XSLT? stateless Programming 1 06-09-2013 06:05 AM
How are opt-in e-mail lists distinguished from spam lists? Travis86 Programming 2 01-29-2012 08:55 PM
[SOLVED] Bash - While Loop reading from two lists simultaneously - nested while loop wolverene13 Programming 11 10-01-2011 05:00 PM
Python and nested lists problem tadeas Programming 2 03-22-2009 05:16 AM
LXer: Unique Sorting Of Lists And Lists Of Lists With Perl For Linux Or Unix LXer Syndicated Linux News 0 09-05-2008 01:50 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 11:54 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration