Solved

XSLT list item selection criteria not working

Posted on 2016-11-08
12
30 Views
Last Modified: 2016-11-08
I have an XSLT stylesheet that generates three types of output: tables, paragraphs and lists. I am able to generate tables and paragraphs, but the list item selection criteria is not working.

Here is the XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">
    
    <xsl:output indent="yes" method="xhtml"/>
    <xsl:strip-space elements="*"/>
     
    <xsl:template match="document">
        <html>
            <head/>
            <body>
                <xsl:apply-templates/>
            </body>
        </html>
    </xsl:template>
    
    
    <xsl:template match="par">
        
        <!-- if immediate preceding-sibling pardef does not have a @list attribute, create a closing tag for unordered list -->
        <xsl:if test="@id and preceding-sibling::pardef[1][not(@list)]">
            <xsl:text disable-output-escaping="yes">&lt;/ul&gt;</xsl:text>
        </xsl:if>
        
        <xsl:choose>
            <!-- if immediate preceding-sibling pardef has a @list attribute, grab @id and create an opening tag for unordered list -->
            <xsl:when test="preceding-sibling::pardef[1][@list = 'bullet']">
                <xsl:variable name="list_id" select="preceding-sibling::pardef[1][@id]" />
                <xsl:text disable-output-escaping="yes">&lt;ul&gt;</xsl:text>
                
                <!-- if par @id matches pardef @id, create a bullet point -->
                <xsl:for-each select="par[@id='$list_id']">
                    <li><xsl:apply-templates select="run"/></li>  
                </xsl:for-each>
            </xsl:when>
            
            <!-- if par does not have @id or does not match pardef @id, create a normal paragraph -->
            <xsl:when test="not(@id='$list_id')">
                <p><xsl:apply-templates select="run"/></p>    
            </xsl:when>     
            
        </xsl:choose>  
        
    </xsl:template>
    
    <xsl:template match="table">
        <table border="1">
            <xsl:for-each select="tablerow">
                <tr>
                    <xsl:for-each select="tablecell">
                        <td>
                            <xsl:apply-templates />
                        </td>
                    </xsl:for-each>
                </tr>
            </xsl:for-each>
        </table>      
    </xsl:template>
    
    <xsl:template match="run">
        <xsl:value-of select="text()" separator=""/>
    </xsl:template>
    
</xsl:stylesheet>

Open in new window


Here is the XML:

<?xml version="1.0" encoding="UTF-8"?>
<document>
    <item name="Some richtext">
        <richtext>
            <pardef/>
            <par def="20">
                <run>This is a </run>
                <run>paragraph.</run>
            </par>
            <table>
                <tablerow>
                    <tablecell>
                        <par def="43"><run>This is a table</run></par></tablecell>
                    <tablecell>
                        <par def="44"><run>This is some data</run></par></tablecell>
                </tablerow>
            </table>
            <pardef id="21" list="bullet"/>
            <par def="21">
                <run>This is a </run>
                <run>bullet point.</run>
            </par>
            <table>
                <tablerow>
                    <tablecell>
                        <par def="43"><run>This is another table</run></par></tablecell>
                    <tablecell>
                        <par def="44"><run>This is some data</run></par></tablecell>
                </tablerow>
            </table>
        </richtext>
    </item>
</document>

Open in new window


And here is the desired output:

    <html>
       <head></head>
      <body>
         <p>This is a paragraph.</p>
         <table border="1">
            <tr>
               <td>This is a table</td>
               <td>This is some data</td>
            </tr>
         </table>
         <ul>
            <li>This is a bullet point.</li>
         </ul>
         <table border="1">
               <tr>
                  <td>This is another table</td>
                  <td>This is some data</td>
               </tr>
         </table>
      </body>
   </html>

Open in new window

0
Comment
Question by:mariita
  • 7
  • 5
12 Comments
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 41879208
I think you are going through a lot of trouble in your stylesheet because your are not telling us a crucial bit of information
... I think you want

           <pardef id="21" list="bullet"/>
            <par def="21">
                <run>This is a </run>
                <run>bullet point.</run>
            </par>
            <par def="21">
                <run>This is a second </run>
                <run>bullet point.</run>
            </par>

To become 1 list with two list items?
Correct?

You are using XSLT2 so you have a lot of options to properly group the list items into lists

One advice.... NEVER EVER use disable output escaping for working around the requierment that an XSLT is wellformed
it is an attempt to do procedural programming in a declarative programming language and it is a headache for maintenance
0
 

Author Comment

by:mariita
ID: 41879220
You're right... there should be one list with two bullet points. I've greatly simplified the various files, and that got lost in translation.
0
 

Author Comment

by:mariita
ID: 41879231
Note: I am working with legacy XML from a Lotus Notes app. The XML is technically well-formed, but it has oddities, such as the closed <pardef list='bullet' /> tag to indicate the start of a list.
0
Master Your Team's Linux and Cloud Stack!

The average business loses $13.5M per year to ineffective training (per 1,000 employees). Keep ahead of the competition and combine in-person quality with online cost and flexibility by training with Linux Academy.

 
LVL 60

Expert Comment

by:Geert Bormans
ID: 41879236
Don't worry, I have done my share of MS Word XML transformations.... it has similar constructs ;-)

Close to making an example working
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 41879258
<?xml version="1.0" encoding="UTF-8"?>
<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 indent="yes" method="xhtml"/>
    <xsl:strip-space elements="*"/>
    
    <xsl:template match="document">
        <html>
            <head/>
            <body>
                <xsl:apply-templates/>
            </body>
        </html>
    </xsl:template>
    
    <xsl:template match="richtext">
        <xsl:for-each-group select="node()" group-starting-with="table | pardef">
            <xsl:choose>
                <xsl:when test="current-group()/self::table">
                    <xsl:apply-templates select="current-group()"/>
                </xsl:when>
                <xsl:when test="current-group()/self::pardef[not(@id)]">
                    <xsl:apply-templates select="current-group()/self::par" mode="p"/>
                </xsl:when>
                <xsl:when test="current-group()/self::pardef[@id][@list = 'bullet']">
                    <ul>
                        <xsl:apply-templates select="current-group()/self::par" mode="li"/>
                    </ul>
                </xsl:when>
            </xsl:choose>
        </xsl:for-each-group>
    </xsl:template>
    
    <xsl:template match="par" mode="p">
        <p>
            <xsl:apply-templates select="node()"/>
        </p>
    </xsl:template>
    
    <xsl:template match="par" mode="li">
        <li>
            <xsl:apply-templates select="node()"/>
        </li>
    </xsl:template>

    <xsl:template match="table">
        <table border="1">
            <xsl:apply-templates select="tablerow"/>
        </table>
    </xsl:template>

    <xsl:template match="tablerow">
        <tr>
            <xsl:apply-templates select="tablecell"/>
        </tr>
    </xsl:template>
    
    <xsl:template match="tablecell">
        <td>
            <xsl:apply-templates />
        </td>
    </xsl:template>
    
    
    <xsl:template match="run">
        <xsl:value-of select="text()" separator=""/>
    </xsl:template>
    
</xsl:stylesheet>

Open in new window


Ignoring the id of the list items, I believe they only play when you have nested lists
(we will tackle that at a next grouping level when we need to... it is hard stuff ;-)
The above stylesheet is definitely something to build on. If the XML is more complex than what you sent,
you will b every happy to have an extensible base... yours was a mess to build on

remember
- avoid for-each as you were doing in the table element as much as possible
- avoid disable-output-escaping AT ALL COST
0
 

Author Comment

by:mariita
ID: 41879279
That worked with the simplified data, but not with the real data, which unfortunately I can't post. I may have oversimplified the XML snippet.

The IDs matter because each document can have multiple lists and each list has a unique ID. In addition, sometimes bullet points are interspersed with <par> elements that should be disregarded because they don't have the ID specific to the list.
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 41879287
well, I can only comment on XML that I actually see
you have posted a required result and I think you have a solid base to work from

I guess, your task now is to either continue yourself
or make a realistic example based on the real data
it is not that hard to keep the structure and cut away the sensitive text
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 41879289
about the ID, that is only a small change,
inside the list group you can call out for only those bullet items with the correct ID
as I said, complexity is not there but in the nested lists if they occur in your data

I am not going to change anything based on vague descriptions, I need hard xml data
0
 

Author Comment

by:mariita
ID: 41879296
The real XML looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="AllTogether11.xslt"?>
<document>
    <item name="Some richtext">
        <richtext>
            <pardef/>
            <par def="20">
                <run>This is a </run>
                <run>paragraph.</run>
            </par>
            <table>
                <tablerow>
                    <tablecell>
                        <par def="43"><run>This is a table</run></par></tablecell>
                    <tablecell>
                        <par def="44"><run>This is some data</run></par></tablecell>
                </tablerow>
            </table>
            <pardef id="21" list="bullet"/>
            <par def="21">
                <run>This is a </run>
                <run>bullet point.</run>
            </par>
            <par />
            <par def="21">
                <run>This is another </run>
                <run>bullet point.</run>
            </par>
            <table>
                <tablerow>
                    <tablecell>
                        <par def="43"><run>This is another table</run></par></tablecell>
                    <tablecell>
                        <par def="44"><run>This is some data</run></par></tablecell>
                </tablerow>
            </table>
            <pardef id="65" list="bullet"/>
            <par def="65">
                <run>This is a </run>
                <run>bullet point.</run>
            </par>
            <par id="20"/>
            <par def="65">
                <run>This is another </run>
                <run>bullet point.</run>
            </par>    
        </richtext>
    </item>
</document>

Open in new window

0
 
LVL 60

Accepted Solution

by:
Geert Bormans earned 500 total points
ID: 41879302
That is not too bad a difference

<?xml version="1.0" encoding="UTF-8"?>
<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 indent="yes" method="xhtml"/>
    <xsl:strip-space elements="*"/>
    
    <xsl:template match="document">
        <html>
            <head/>
            <body>
                <xsl:apply-templates/>
            </body>
        </html>
    </xsl:template>
    
    <xsl:template match="richtext">
        <xsl:for-each-group select="node()" group-starting-with="table | pardef">
            <xsl:choose>
                <xsl:when test="current-group()/self::table">
                    <xsl:apply-templates select="current-group()"/>
                </xsl:when>
                <xsl:when test="current-group()/self::pardef[not(@id)]">
                    <xsl:apply-templates select="current-group()/self::par" mode="p"/>
                </xsl:when>
                <xsl:when test="current-group()/self::pardef[@id][@list = 'bullet']">
                    <xsl:variable name="this-id" select="current-group()/self::pardef/@id"/>
                    <ul>
                        <xsl:apply-templates select="current-group()/self::par[@def = $this-id]" mode="li"/>
                    </ul>
                </xsl:when>
            </xsl:choose>
        </xsl:for-each-group>
    </xsl:template>
    
    <xsl:template match="par" mode="p">
        <p>
            <xsl:apply-templates select="node()"/>
        </p>
    </xsl:template>
    
    <xsl:template match="par" mode="li">
        <li>
            <xsl:apply-templates select="node()"/>
        </li>
    </xsl:template>

    <xsl:template match="table">
        <table border="1">
            <xsl:apply-templates select="tablerow"/>
        </table>
    </xsl:template>

    <xsl:template match="tablerow">
        <tr>
            <xsl:apply-templates select="tablecell"/>
        </tr>
    </xsl:template>
    
    <xsl:template match="tablecell">
        <td>
            <xsl:apply-templates />
        </td>
    </xsl:template>
    
    
    <xsl:template match="run">
        <xsl:value-of select="text()" separator=""/>
    </xsl:template>
    
</xsl:stylesheet>

Open in new window

0
 

Author Comment

by:mariita
ID: 41879312
Thanks! :)
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 41879365
welcome,
let me know when you have issues building on the above
0

Featured Post

Master Your Team's Linux and Cloud Stack

Come see why top tech companies like Mailchimp and Media Temple use Linux Academy to build their employee training programs.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

I was working on a PowerPoint add-in the other day and a client asked me "can you implement a feature which processes a chart when it's pasted into a slide from another deck?". It got me wondering how to hook into built-in ribbon events in Office.
Many times as a report developer I've been asked to display normalized data such as three rows with values Jack, Joe, and Bob as a single comma-separated string such as 'Jack, Joe, Bob', and vice versa.  Here's how to do it. 
This video shows how to use Hyena, from SystemTools Software, to bulk import 100 user accounts from an external text file. View in 1080p for best video quality.
Nobody understands Phishing better than an anti-spam company. That’s why we are providing Phishing Awareness Training to our customers. According to a report by Verizon, only 3% of targeted users report malicious emails to management. With compan…

831 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question