Question

I am currently trying to implement a function to sort a XML file by the attribute of one of its nodes.

The way to go seemed to be using XSLT to transform the XML file and by researching a bit I managed to put together a working XSLT file (validated in some online-validator, output was as would be expected).

After I got the results I wanted, I started implementing JavaScript functions to do this automatically every time it is required. Ultimately a chart (amCharts) will be created using the data from the XML file. To generate that chart properly, the data has to be sorted though.

Right now everything seems to work syntax-wise, however, the XSLTProcessor and/or XMLSerializer seem to have problems parsing/combining both the XML and XLST file.

Strangely enough Firefox also throws an error: Component returned failure code: 0x80600001 [nsIXSLTProcessor.importStylesheet]

Chrome doesn't seem to have this problem.

But this is not the main issue. The result generated contains a rudimental HTML page saying that there was an error, but giving no exact error description/location/explanation whatsoever.

I expected to see the sorted XML file here, just as in the online XML/XSLT validator, which had the same inputs XML/XSLT content wise.

Does anyone have an idea regarding the source of this issue? Would greatly appreciate any hints and/or solutions to this.

best regards,

daZza

Edit

After researching the "Extra content at the end of the document" error on the generated page more in-depth (and having overlooked it when I posted the OP), it seems that the error is triggered when there are more than one root elements in the XML file, which would obviously be wrong.

However, the source XML files do not have multiple root nodes and are all perfectly valid.

This leads me to believe that the XSLT reordering/sorting of the source file is not done as expected, although it looked good in the XSLT validator. I guess somehow it produces multiple root nodes instead of just reordering the item nodes within the root node?

Unfortunately I am no expert regarding XSLT so it would be awesome, if someone with a more detailed knowledge could especially look at the XSLT code.

Edit2

I think I might have solved the problem by changing the XSLT query slightly. I am getting a proper looking output for Chrome and Firefox, but IE still throws an error (did I mention that I hate cross-browser compatibilities? -.-).

Further testing is still required, but at least it's a progress. Still appreciating any hints regarding the topic though.

Code:

XML Sample Snippet (removed content, the "" parts are filled in the real file):

<?xml version="1.0" encoding="utf-8"?>
<root>
    <item Art="" LinkTitle="" Eindruck="" Bereich="" Unterbereich="" Priority="" Handlungsma_x00df_nahme="" Status_x0020_der_x0020_Ma_x00df_="" Aufwand="" Benefit="" Termin_x0020_der_x0020_Retrospek="" Produkt="" Release="" />
    <item Art="" LinkTitle="" Eindruck="" Bereich="" Unterbereich="" Priority="" Handlungsma_x00df_nahme="" Status_x0020_der_x0020_Ma_x00df_="" Aufwand="" Benefit="" Termin_x0020_der_x0020_Retrospek="" Produkt="" Release="" />
</root>

XSLT file

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:choose>
                <xsl:when test="*[local-name()='item']">
                    <xsl:apply-templates select="@* | node()">
                        <xsl:sort select="@Unterbereich" />
                    </xsl:apply-templates>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:apply-templates select="@* | node()" />
                </xsl:otherwise>
            </xsl:choose>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

JavaScript function

function sortXML()
{
    var parser = new DOMParser();

    // xml and xsl are correctly filled with the data from their appropriate files 
    // via AJAX GET requests earlier in the code
    var domToBeTransformed = xml;
    var xslt = xsl;
    var processor = new XSLTProcessor();

    processor.importStylesheet(xslt);

    var newDocument = processor.transformToDocument(domToBeTransformed);
    var serializer = new XMLSerializer();
    var newDocumentXml = serializer.serializeToString(newDocument);

    alert(newDocumentXml);
}

Currently generated document (var newDocumentXml)

<html xmlns="http://www.w3.org/1999/xhtml">
    <body>
        <parsererror style="display: block; white-space: pre; border: 2px solid #c77; padding: 0 1em 0 1em; margin: 1em; background-color: #fdd; color: black">
        <h3>This page contains the following errors:</h3>
        <div style="font-family:monospace;font-size:12px">error on line 1 at column 1: Extra content at the end of the document</div>
        <h3>Below is a rendering of the page up to the first error.</h3>
        </parsererror>
    </body>
</html> 
Was it helpful?

Solution

Firstly, I would re-jig your XSLT to avoid the use of xsl:choose. There is no point in testing if a child element exists if you are already matching an attribute or text node. It may be better (and cleaner, with less indentation) to use separate templates.

Try this XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>

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

    <xsl:template match="*[*[local-name()='item']]">
        <xsl:copy>
            <xsl:apply-templates select="@*" />
            <xsl:apply-templates select="node()">
                <xsl:sort select="@Unterbereich" />
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

The first thing to note is that you specified a version of "2.0" in your XSLT. Microsoft (and so IE) does not natively support XSLT 2.0. However, in this case, you are not using any XSLT 2.0 features, so changing it to XSLT 1.0 should not be a problem (Of course, if your actual XSLT does need to use 2.0, then that is a whole separate question!)

But I don't think this is an issue with your XSLT any more, but more an issue with how transformation is done in the browser with javascript. The code you have given will only work in Chrome and Firefox (and Opera, I think). IE doesn't support those commands, and has its own methods. And to compound matters further, IE10 and above do things differently to previous versions!

Anyway, I headed over to Object doesn't support property or method 'transformNode' in Internet Explorer 10 (Windows 8) and found a good sample of JavaScript. Try re-jigging your JavaScript to this:

function sortXML()
{
  if (typeof (XSLTProcessor) != "undefined") 
  {
      var xsltProcessor = new XSLTProcessor();
      xsltProcessor.importStylesheet(xsl);
      var resultDocument = xsltProcessor.transformToDocument(xml);
      var serializer = new XMLSerializer();
      var newDocumentXml = serializer.serializeToString(resultDocument);
      alert(newDocumentXml);
  }
  else if (typeof (xml.transformNode) != "undefined") 
  {
      var ex = xml.transformNode(xsl);
      alert(ex);
  }
  else
  {
     var xslDoc = new ActiveXObject("Msxml2.FreeThreadedDOMDocument");
     xslDoc.load(xsl);

     var xslt = new ActiveXObject("Msxml2.XSLTemplate");
     xslt.stylesheet = xslDoc;

     var xslProc = xslt.createProcessor();
     xslProc.input = xml;
     xslProc.transform();

     alert(xslProc.output);
  }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top