Search code examples
xmlxsltcoldfusioncoldfusion-9

coldfusion xmltransform displays as one big block


I'm playing around with XML and XSL. I have an XMLfile that looks somewhat like this:

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="/_xslt/xslt_terms.xsl" type="text/xsl" ?>
    <terms>
       <term><p>Use of Website</p>
           <term>
                <term><p>wording here</p></term>
                <term><p>more words!</p></term>
           </term>
        <term><p>serious words</p></term>
    </terms>

and an XSL file that looks like

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" >

<xsl:template match="term">
    <xsl:number level="multiple" format="1. "/>
    <xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>

The output should display like

 1. Use of Website
    1.1. 
        1.1.1. wording here
        1.1.2. more words

    1.2. serious words

but when i run

<cfoutput>
     <cffile action="read" 
             file="#application.sPath#_xslt/xslt_terms.xsl" 
             variable="variables.xmltrans">
     <cfset variables.xmldoc = XmlParse("#application.sPath#_templates/_ajax/_terms/xml_terms.xml")>
     #XMLTransform(variables.xmldoc, variables.xmltrans)#
</cfoutput>

i get a giant block of text with no line breaks. so it looks like:

 1. Use of Website 1.1. 1.1.1. wording here 1.1.2. more words 1.2. serious words

As i say, this is the first time i'm playing around with XML and XSL since.. a long time, so it's likely i've missed something

EDIT

I found some code to help which nearly does the job. I've changed my xml to remove all <p> tags, and i've changed my xsl file to read:

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

    <xsl:template match="@*|node()">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
    </xsl:template>

<xsl:template match="term">
    <p>
        <xsl:number format="1." level="multiple"/>
        <xsl:apply-templates select="@*|node()"/>
    </p>
</xsl:template>

This nearly does it, but i'm losing the indentation, is there anything i can do to make it indented like above?


Solution

  • If you can make sure that the complete HTML document your XSLT output is injected in can include some CSS then I would suggest to create a HTML ordered, nested list where the numbers are done with CSS:

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        version="1.0">
    
        <xsl:strip-space elements="*"/>
        <xsl:output method="html" indent="yes" version="5.0"/>
    
        <xsl:template match="/">
            <html>
                <head>
                    <title>list test</title>
                    <style>
                        ol.nested  { counter-reset: section; list-style-type: none; }
                        ol.nested li { counter-increment: section; }
                        ol.nested li:before { content: counters(section, ".") ". "; }
                    </style>
                </head>
                <body>
                    <xsl:apply-templates/>
                </body>
            </html>
        </xsl:template>
    
        <xsl:template match="terms[term]">
            <ol class="nested">
                <xsl:apply-templates/>
            </ol>
        </xsl:template>
    
        <xsl:template match="term">
            <li>
                <xsl:apply-templates select="node()[not(self::term)]"/>
                <xsl:if test="term">
                    <ol class="nested">
                        <xsl:apply-templates select="term"/>
                    </ol>
                </xsl:if>
            </li>
        </xsl:template>
    </xsl:stylesheet>
    

    With the input XML being

    <?xml-stylesheet type="text/xsl" href="sheet.xsl"?>
    <terms>
        <term><p>Use of Website</p>
            <term>
                <term><p>wording here</p></term>
                <term><p>more words!</p></term>
            </term>
            <term><p>serious words</p></term>
        </term>
    </terms>
    

    modern browsers will render that as a nested listed with the counting you want.

    If you want to create the numbers with XSLT then I would still create a HTML list as the HTML needs to be structured:

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        version="1.0">
    
        <xsl:strip-space elements="*"/>
        <xsl:output method="html" indent="yes" version="5.0"/>
    
        <xsl:template match="terms[term]">
            <ol style="list-style-type: none;">
                <xsl:apply-templates/>
            </ol>
        </xsl:template>
    
        <xsl:template match="term">
            <li><xsl:number level="multiple" format="1. "/>
                <xsl:apply-templates select="node()[not(self::term)]"/>
                <xsl:if test="term">
                    <ol style="list-style-type: none;">
                        <xsl:apply-templates select="term"/>
                    </ol>
                </xsl:if>
            </li>
        </xsl:template>
    </xsl:stylesheet>
    

    Now when applied to the input

    <?xml-stylesheet type="text/xsl" href="sheet.xsl"?>
    <terms>
        <term><p>Use of Website</p>
            <term>
                <term><p>wording here</p></term>
                <term><p>more words!</p></term>
            </term>
            <term><p>serious words</p></term>
        </term>
    </terms>
    

    you get the numbering you want.