Sunday, November 21, 2010

XMLHttpRequest.responseXML returns null

Issue: [JavaScript] Loading XSLT via XMLHttpRequest.responseXML always results in null.

Reason: It is due to incorrect MIME type "application/octet-stream" returned by the HTTP response found in "Content-Type" response header.

   var responseMIMEType = xhttp.getResponseHeader('Content-Type'); // xhttp is XMLHttpRequest object.

According to the XMLHttpRequest specification 3.7.5, XMLHttpRequest.responseXML will return null if the HTTP response header 'content-type' is not XML.

Resolution: If resultant responseXML is null, we could force to override the MIME type or convert XML text to XML DOM.

  • For the latest modern browsers like Mozilla (both SeaMonkey 2.0.10 and Firefox 3.5), Safari 5.0 and Opera 10.51, we could simply instruct the browser to override the MIME Type when it receives the document. Do this before XMLHttpRequest.send.
        // Override MIME type.  Set this before send. 
        if (xhttp.overrideMimeType)   xhttp.overrideMimeType('text/xml');
  • For others except IE, we could set up another defend mechanism by converting XML string to DOM with DOMParser.
        // Create XML DOM from string
        xmlDoc = (new DOMParser()).parseFromString(xhttp.responseText, 'text/xml');
  • IE doesn't support MIME overridden. And you may not be able to use DOMParser to parse XML string either. Otherwise, you may step into another error.

    The stylesheet does not contain a document element. The stylesheet may be empty, or it may not be a well-formed XML document.

    If it is the case, for IE, do this:

     xmlDoc = new ActiveXObject('Microsoft.XMLDOM');
     xmlDoc.async = false;
     xmlDoc.loadXML(xhttp.responseText);
    

    The code for loading XML and XSLT may look like the following:

      var xhttp;
      function loadXMLDoc(url) {
        if (window.XMLHttpRequest) {
          xhttp = new XMLHttpRequest();
        }
        else {
          xhttp = new ActiveXObject('Microsoft.XMLHTTP');
        }
        
        xhttp.open('GET', url, false);  
        
        if (xhttp.overrideMimeType) 
          xhttp.overrideMimeType('text/xml');       
    
        xhttp.send(null);
     
    var xmlDoc = xhttp.responseXML; if (!xmlDoc) { if (isIE) { xmlDoc = new ActiveXObject('Microsoft.XMLDOM'); xmlDoc.async = false; xmlDoc.loadXML(xhttp.responseText); } else xmlDoc = (new DOMParser()).parseFromString(xhttp.responseText, 'text/xml'); }
    return xmlDoc; }
    One more thing, if you run or test this code under a file-system-based environment such as Visual Studio instead of being served by a Web server, the above stylesheet error on IE may still occur. To resolve this, both XML and XSLT must be loaded by the same method like this:
        var xmlDoc;
        if (isIE) {
          xmlDoc = new ActiveXObject('Microsoft.XMLDOM');
          xmlDoc.async = false;
          xmlDoc.loadXML(xhttp.responseText);
        }
        else {
          xmlDoc = xhttp.responseXML;
          if (!xmlDoc) {
            xmlDoc = (new DOMParser()).parseFromString(xhttp.responseText, 'text/xml');
          }
        }

After the XML and the XSLT have successfully been loaded, we can transform the XML using the loaded XSLT.

  // Transform XML using XSLT
  // Here both xml and xslt are xml dom objects
  function getTransformedXML(xml, xslt) {
    if (window.ActiveXObject)  { // for IE
       return xml.transformNode(xslt);      
    }
    // for Mozilla (SeaMonkey and FireFox), Safari, Opera, etc.
    else if (document.implementation && document.implementation.createDocument) {
      var xsltProcessor = new XSLTProcessor();
      xsltProcessor.importStylesheet(xslt);
      return xsltProcessor.transformToFragment(xml, document);
    }
     
    return null;
  }

The complete code

Here the code is revised a bit to locate the MS latest XML parser for use instead of using the default old version, Microsoft.XMLDOM.
  var xhttp;
  function loadXMLDoc(url) {
    if (window.XMLHttpRequest) {
      xhttp = new XMLHttpRequest();
    }
    else {
      xhttp = new ActiveXObject('Microsoft.XMLHTTP');
    }
    
    xhttp.open('GET', url, false);  

    if (xhttp.overrideMimeType) 
      xhttp.overrideMimeType('text/xml');       

    xhttp.send(null);

    if (isIE) {
      xmlDoc = getMSXmlParser();
      xmlDoc.async = false;
      xmlDoc.loadXML(xhttp.responseText);
    }
    else {
      xmlDoc = xhttp.responseXML;
      if (!xmlDoc) {
        xmlDoc = (new DOMParser()).parseFromString(xhttp.responseText, 'text/xml');
      }
    }
    return xmlDoc;
  }

  // Transform XML using XSLT
  // Here both xml and xslt are xml dom objects
  function getTransformedXML(xml, xslt) {
    if (window.ActiveXObject)  { // for IE
       return xml.transformNode(xslt);      
    }
    // for Mozilla (SeaMonkey and FireFox), Safari, Opera, etc.
    else if (document.implementation && document.implementation.createDocument) {
      var xsltProcessor = new XSLTProcessor();
      xsltProcessor.importStylesheet(xslt);
      return xsltProcessor.transformToFragment(xml, document);
    }
     
    return null;
  }
  
function getMSXmlParser() { var parser = ['Msxml2.DOMDocument.6.0', 'Msxml2.DOMDocument.5.0', 'Msxml2.DOMDocument.4.0', 'Msxml2.DOMDocument.3.0', 'MSXML2.DOMDocument', 'Microsoft.XMLDOM']; // the same as MSXML.DOMDocument for (var i in parser) { try { var xParser = new ActiveXObject(parser[i]); if (xParser) { return xParser; } } catch (e) {} } return null; }

7 comments:

  1. Hi I'm trying to get a simple solution up and running for this as a test. I am trying to combine your code with the w3schools code but I keep getting the error "The stylesheet does not contain a document element. The stylesheet may be empty, or it may not be a well-formed XML document." Not sure why as I have used your example.

    The script on my page is:


    var xhttp;
    var isIE = (navigator.appName=="Microsoft Internet Explorer");
    function loadXMLDoc(url) {
    if (window.XMLHttpRequest) {
    xhttp = new XMLHttpRequest();
    }
    else {
    xhttp = new ActiveXObject('Microsoft.XMLHTTP');
    }

    xhttp.open('GET', url, false);

    if (xhttp.overrideMimeType)
    xhttp.overrideMimeType('text/xml');

    xhttp.send(null);

    if (isIE) {
    xmlDoc = getMSXmlParser();
    xmlDoc.async = false;
    xmlDoc.loadXML(xhttp.responseText);
    }
    else {
    xmlDoc = xhttp.responseXML;
    if (!xmlDoc) {
    xmlDoc = (new DOMParser()).parseFromString(xhttp.responseText, 'text/xml');
    }
    }
    return xmlDoc;
    }

    function displayResult()
    {
    xml=loadXMLDoc("cdcatalog.xml");
    xsl=loadXMLDoc("cdcatalog.xsl");
    // code for IE
    if (window.ActiveXObject)
    {
    ex=xml.transformNode(xsl);
    document.getElementById("example").innerHTML=ex;
    }
    // code for Mozilla, Firefox, Opera, etc.
    else if (document.implementation && document.implementation.createDocument)
    {
    xsltProcessor=new XSLTProcessor();
    xsltProcessor.importStylesheet(xsl);
    resultDocument = xsltProcessor.transformToFragment(xml,document);
    document.getElementById("example").appendChild(resultDocument);
    }
    }

    function getMSXmlParser() {

    var parser = ['Msxml2.DOMDocument.6.0',

    'Msxml2.DOMDocument.5.0',

    'Msxml2.DOMDocument.4.0',

    'Msxml2.DOMDocument.3.0',

    'MSXML2.DOMDocument',

    'Microsoft.XMLDOM']; // the same as MSXML.DOMDocument

    for (var i in parser) {

    try {

    var xParser = new ActiveXObject(parser[i]);

    if (xParser) {
    return xParser;

    }

    }

    catch (e) {}

    }

    return null;

    }

    As you can see it is pretty basic.

    The xml and xsl are taken from the w3schools client side example.

    Any help would be greatly appreciated.

    ReplyDelete
  2. Great!!!

    Your post really helped me.

    Thanks a lot.

    ReplyDelete
  3. Brilliant - been struggling with this and came across your solution today - fixed!

    ReplyDelete
  4. Thanks dear. It worked perfectly for me.

    ReplyDelete
  5. after implementing I get the result in autocomplete textbox but I am unable to select those records. Please help

    ReplyDelete