Solved

XSLT list item selection criteria not working

Posted on 2016-11-08
12
45 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
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

Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

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

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, …
Come and listen to Percona CEO Peter Zaitsev discuss what’s new in Percona open source software, including Percona Server for MySQL (https://www.percona.com/software/mysql-database/percona-server) and MongoDB (https://www.percona.com/software/mongo-…
Monitoring a network: how to monitor network services and why? Michael Kulchisky, MCSE, MCSA, MCP, VTSP, VSP, CCSP outlines the philosophy behind service monitoring and why a handshake validation is critical in network monitoring. Software utilized …

726 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