javascriptxslthtml-escape-characters

Using a reserved character (<) in JS with XSLT


I need to adapt an old XSLT file that transforms an XML file into HTML/JS. It doesn't work anymore.

Here is the original JS code generated in XSLT file :

function processVisibility(item, visible) {
    var childs = item.childNodes;
    for (var i = 0; i &lt; childs.length; i++) {
        if ( (childs.item(i).className == "ArchiveObject") 
            || (childs.item(i).className == "Document")
            || (childs.item(i).className == "Attachment") ) {
            if (visible) {
                childs.item(i).style.display = "block";
            } else {
                childs.item(i).style.display = "none";
            }
        }   
    }
}

The string "&lt ;" is not transformed into < in the output HTML. As a result, JS code doesn't work in modern browsers.

If I replace "&lt ;" with < in the XSLT file, I get an error and that's normal. So I tried creating a variable:

<xsl:variable name="chevron">&lt;</xsl:variable>

and

function processVisibility(item, visible) {
    var childs = item.childNodes;
    for (var i = 0; i <xsl:value-of select="$chevron" disable-output-escaping="yes"/> childs.length; i++) {
        if ( (childs.item(i).className == "ArchiveObject") 
            || (childs.item(i).className == "Document")
            || (childs.item(i).className == "Attachment") ) {
            if (visible) {
                childs.item(i).style.display = "block";
            } else {
                childs.item(i).style.display = "none";
            }
        }   
    }
}

But the HTML output does not contain < but "&lt ;" How can I get < to be generated in HTML using XSLT?

I use the HTML serialization method but it doesn't work. Here is the beginning of the XSLT file:

<xsl:stylesheet version="1.0"
    xmlns:sada="sada:v1.0"
    xmlns="http://www.w3.org/1999/xhtml"
               xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
               xmlns:xsd="http://www.w3.org/2001/XMLSchema"
               xmlns:ccts="urn:un:unece:uncefact:documentation:standard:CoreComponentsTechnicalSpecification:2"
    exclude-result-prefixes="sada xsl xsd ccts">

    <xsl:output method="html" indent="yes" media-type="text/html" encoding="UTF-8"/>

    <xsl:template match="/">
        <html>
            <head>
                
                <script type="text/javascript">
function processVisibility(item, visible) {
    var childs = item.childNodes;
    for (var i=0; i&lt;childs.length; i++) {
        if ( (childs.item(i).className == "ArchiveObject") 
            || (childs.item(i).className == "Document")
            || (childs.item(i).className == "Attachment") ) {
            if (visible) {
                childs.item(i).style.display = "block";
            } else {
                childs.item(i).style.display = "none";
            }
        }   
    }
}           </script>
                <title>Standard (SADA)</title>
            </head>
            <body>
                <xsl:apply-templates />
            </body>
        </html>
    </xsl:template>
</xsl:stylesheet>``` 
and un exemple of xml source : 

```<?xml version="1.0" encoding="UTF-8"?>
<ArchiveTransfer xmlns="sada:v1.0" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#" xmlns:premis="info:lc/xmlns/premis-v2" xmlns:ns4="http://www.w3.org/1999/xlink" xmlns:ns5="http://www.w3.org/2001/XMLSchema-instance" Id="_20240314211030158" ns5:schemaLocation="sada:v1.0 sada_v1-0_archivetransfer.xsd info:lc/xmlns/premis-v2 http://www.loc.gov/premis/v2/premis.xsd">
    <Date>2024-05-01T22:37:26.047+02:00</Date>
    <TransferIdentifier/>
</ArchiveTransfer>```

Solution

  • It is not clear what your problem is but in general, if the xsl:output method="html" and you have a script result element in no namespace and the XSLT processor is in charge of serialization it will ensure that the output of e.g.

    <?xml version="1.0" encoding="utf-8"?>
    <html lang="en">
      <head>
        <title>XSLT fiddle test</title>
        <script>
    function processVisibility(item, visible) {
        var childs = item.childNodes;
        for (var i = 0; i &lt; childs.length; i++) {
            if ( (childs.item(i).className == "ArchiveObject") 
                || (childs.item(i).className == "Document")
                || (childs.item(i).className == "Attachment") ) {
                if (visible) {
                    childs.item(i).style.display = "block";
                } else {
                    childs.item(i).style.display = "none";
                }
            }   
        }
    }
        </script>
      </head>
      <body>
        <h1>Test</h1>
      </body>
    </html>
    

    by e.g.

    <?xml version="1.0" encoding="utf-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      version="1.0">
    
      <xsl:output method="html" indent="yes" version="5" doctype-system="about:legacy-doctype"/>
    
      <xsl:template match="@* | node()">
        <xsl:copy>
          <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
      </xsl:template>
    
    </xsl:stylesheet>
    

    is

    <!DOCTYPE html SYSTEM "about:legacy-doctype">
    <html lang="en">
      <head>
        <META http-equiv="Content-Type" content="text/html; charset=utf-16">
        <title>XSLT fiddle test</title>
        <script>
    function processVisibility(item, visible) {
        var childs = item.childNodes;
        for (var i = 0; i < childs.length; i++) {
            if ( (childs.item(i).className == "ArchiveObject") 
                || (childs.item(i).className == "Document")
                || (childs.item(i).className == "Attachment") ) {
                if (visible) {
                    childs.item(i).style.display = "block";
                } else {
                    childs.item(i).style.display = "none";
                }
            }   
        }
    }
        </script>
      </head>
      <body>
        <h1>Test</h1>
      </body>
    </html>
    

    where you can see that the script element contains < and not &lt;.

    As for the code you have added, well, it uses xsl:output method="html" in an XSLT 1.0 stylesheet but puts its result elements in the XHTML namespace instead of having them in no namespace, that way a script element in that namespace is serialized even with that output method as an XML element and according to XML rules.

    If you want to use XSLT to create XHTML as text/html then you really should be using an XSLT 2 or 3 processor with xsl:output method="xhtml"; but you will need to put any JavaScript into an external file to prevent the issue with < being serialized as &lt; as https://www.w3.org/TR/xhtml1/#C_4 states: "Use external scripts if your script uses < or & or ]]> or --".

    In the end, in most cases you have it way easier if your text/html HTML elements are in no namespace.