Solved

XSLT list item selection criteria not working

Posted on 2016-11-08
12
20 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
Comment Utility
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
Comment Utility
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
Comment Utility
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
 
LVL 60

Expert Comment

by:Geert Bormans
Comment Utility
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
Comment Utility
<?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
Comment Utility
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
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 60

Expert Comment

by:Geert Bormans
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
Thanks! :)
0
 
LVL 60

Expert Comment

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

Featured Post

What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

Join & Write a Comment

Suggested Solutions

Browsing the questions asked to the Experts of this forum, you will be amazed to see how many times people are headaching about monster regular expressions (regex) to select that specific part of some HTML or XML file they want to extract. The examp…
The Confluence of Individual Knowledge and the Collective Intelligence At this writing (summer 2013) the term API (http://dictionary.reference.com/browse/API?s=t) has made its way into the popular lexicon of the English language.  A few years ago, …
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, Just open a new email message.  In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…
Internet Business Fax to Email Made Easy - With eFax Corporate (http://www.enterprise.efax.com), you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, fr…

744 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

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now