使用 XSL(Chrome、Firefox 和 IE 8/11)跨浏览器 XML 操作和解析

Crossbrowser XML manipulation and parse with XSL (Chrome, Firefoz and IE 8/11)

我有一个表单,其中我得到一个 XML,在用户填写表单时对其进行操作,并最终将此操作的 XML 转换为 XSL。我需要此解决方案在没有兼容模式的 Chrome、Firefox 和 Internet Explorer 8 到 11 中工作。

我用我在 IE 9(或至少没有用户抱怨)、Chrome 和 Firefox 上成功运行的代码编写了一个 JSFiddle。但我在 IE 11 上遇到问题。当我的代码尝试从 ActiveXObject 执行 transformNode 时。我已经尝试了一些我在几个站点(包括此处)中找到的建议,但是当我的代码试图操纵 XML 时,有些根本不起作用或开始抛出异常。所以我需要帮助让这段代码正常工作。

这是 JSFiddle:https://jsfiddle.net/mfedatto/6nsc5bf1/

HTML

<div id="wrapper"></div>
<div id="console"></div>

JavaScript

try {
  function getXmlDom(content) {
    var xmlDom;

    if (typeof window.DOMParser != "undefined") {
      trace.push("Creating DOMParser");

      xmlDom = (new window.DOMParser()).parseFromString(content, "text/xml");

      trace.push("DOMParser created");
    }
    else if (typeof window.ActiveXObject != "undefined" && new window.ActiveXObject("Microsoft.XMLDOM")) {
      try {
        trace.push("Creating MSXML2.DOMDocument.6.0");

        xmlDoc = new ActiveXObject("MSXML2.DOMDocument.6.0");

        trace.push("MSXML2.DOMDocument.6.0 created");
      }
      catch (ex) {
        trace.push("Creating Microsoft.XMLHTTP");

        xmlDoc = new ActiveXObject("Microsoft.XMLHTTP");

        trace.push("Microsoft.XMLHTTP created");
      }

      xmlDom.async = "false";

      try { xmlDoc.responseType = "msxml-document"; } catch (ex) { }

      trace.push("Loading XML content");

      xmlDom.loadXML(content);

      trace.push("XML content loaded");
    }
    else {
      throw new Error("No XML parser found");
    }

    return xmlDom;
  }

  function xslTransformTo(xsl, xml, wrapper) {
    if ((window.ActiveXObject) || "ActiveXObject" in window) {
      trace.push("Transforming with ActiveXObject");

      wrapper.innerHTML = xml.transformNode(xsl);

      trace.push("Transformed with ActiveXObject");
    }
    else if (document.implementation && document.implementation.createDocument) {
      trace.push("Transforming with XSLTProcessor");

      xsltProcessor = new XSLTProcessor();

      xsltProcessor.importStylesheet(xsl);

      wrapper.appendChild(xsltProcessor.transformToFragment(xml, document));

      trace.push("Transformed with XSLTProcessor");
    }
    else {
      throw new Error("No XSL parser found");
    }
  }

  function xmlString(xml) {
    return (new XMLSerializer()).serializeToString(xml);
  }

  function showError(ex) {
    var console = document.getElementById("console");

    showTrace();

    console.appendChild(document.createTextNode("ERROR!!! " + ex.message));
  }

  function showTrace() {
    var console = document.getElementById("console");

    for (var i = 0; i < trace.length; i++) {
      console.appendChild(document.createTextNode(trace[i]));
      console.appendChild(document.createElement("br"));
    }
  }

  var trace = [];
  var strXml = "<root>\n"
             + "  <fc>\n"
             + "    <sc>\n"
             + "      <i />\n"
             + "      <i />\n"
             + "      <i />\n"
             + "      <i />\n"
             + "    </sc>\n"
             + "  </fc>\n"
             + "</root>";

  trace.push("XML string defined");

  var strXsl = "<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n"
             + "  <xsl:template match=\"/root\">\n"
             + "    <ul>\n"
             + "      <xsl:for-each select=\"fc/sc/i\">\n"
             + "        <li>[<xsl:value-of select=\"position()\" />]</li>\n"
             + "      </xsl:for-each>\n"
             + "    </ul>\n"
             + "  </xsl:template>\n"
             + "</xsl:stylesheet>";

  trace.push("XSL string defined");

  var strWrapperId = "wrapper";

  trace.push("Parsing XML");

  var xmlDoc = getXmlDom(strXml);

  trace.push("XML parsed");
  trace.push("Parsing XSL");

  var xslDoc = getXmlDom(strXsl);

  trace.push("XML parsed");

  var domWrapper = document.getElementById(strWrapperId);
  var xmlItemList = xmlDoc.getElementsByTagName("i");

  trace.push("All variables loaded");
  trace.push("Iterating item positions");

  try {
    for (var i = 0; i < xmlItemList.length; i++) {
      xmlItemList[i].setAttribute("p", i);
    }
  }
  catch (ex) {
    showError(ex);

    return;
  }

  trace.push("Itens positions iterared");
  trace.push("Transforming XML with XSL to wrapper");

  try {
    xslTransformTo(xslDoc, xmlDoc, domWrapper);
  }
  catch (ex) {
    showError(ex);

    return;
  }

  showTrace();
}
catch (ex) {
    alert(ex.message);
}

P.S.: 与我使用的代码的唯一区别是跟踪和基于字符串内容的 loadXML,事实上我使用 load 方法和可访问的 uri.

编辑 1 - 23/fev/16

我尝试 运行 Martin Honnen 在 IE 11 上发布的 JSFiddle,它显示 ERROR!!! No XSL parser found。我使用他建议的 MSDN 页面上的说明进行了一些重构。那篇文章指示尝试,而不是测试,因为某些插件可能被实例化但未被检测到。然后我的 IE 11 使用 DOMParser 创建并加载我的 XMLXSL 对象和内容,但无法同时使用 transformNodeXSLTProcessor 进行转换。正如在:http://jsfiddle.net/mfedatto/6nsc5bf1/38

编辑 2 - 25/fev/16

由于某种原因,该解决方案不适用于 JSFiddle,我将对此进行一些挖掘。但是有了 Martin Honnen 的回答,我可以加载我的外部 XMLXSL 文件跨浏览器,包括 IE 11。这是我使用的最终代码:

function loadXmlFile(path) {
    var xmlDoc;

    if (loadXmlFile.cache === undefined) {
        loadXmlFile.cache =  { };
    }

    if (loadXmlFile.cache[path] === undefined) {
        try {
            xmlDoc = new ActiveXObject("MSXML2.DOMDocument.6.0");
        }
        catch (e) {
            try {
                xmlDoc = new ActiveXObject("MSXML2.DOMDocument.3.0");
            }
            catch (e2) {
                xmlDoc = new XMLHttpRequest();

                xmlDoc.responseType = "application/xml";
            }
        }

        try {
            xmlDoc.open("GET", path, false);
            xmlDoc.send();

            loadXmlFile.cache[path] = xmlDoc.responseXML;
        }
        catch (ex) {
            xmlDoc.async = false;
            xmlDoc.load(path);

            loadXmlFile.cache[path] = xmlDoc;
        }
    }

    return loadXmlFile.cache[path];
}

function xslTransformTo(xsl, xml, wrapper) {
    if (typeof xml.transformNode != "undefined") {
        wrapper.innerHTML = xml.transformNode(xsl);
    }
    else if (typeof XSLTProcessor != "undefined") {
        xsltProcessor = new XSLTProcessor();

        xsltProcessor.importStylesheet(xsl);

        wrapper.appendChild(xsltProcessor.transformToFragment(xml, document));
    }
}

请参阅 https://msdn.microsoft.com/en-us/library/dn423948(v=vs.85).aspx,在 IE 11 中,您需要在 try/catch 中使用 new ActiveXObject('program.id'),window 属性 的检查不起作用.

此外,您在 IE 中使用 DOMParser 创建的本机 IE DOM 文档不支持 transformNode,因此如果您知道需要 XML DOM 文件在 IE 中进行 XSLT 转换然后你需要确保你创建一个 MSXML DOM 文件 new ActiveXObject 并且你需要首先在你的代码中尝试这样做使用 try/catch,在您尝试实例化 DOMParser.

之前

所以对于 XML 从字符串解析我会使用

  function getXmlDom(content) {
    var xmlDom;
    try {
      xmlDom = new ActiveXObject('Msxml2.DOMDocument.6.0');
      xmlDom.loadXML(content);
    }
    catch (e) {
        try {
        xmlDom = new ActiveXObject('Msxml2.DOMDocument.3.0');
        xmlDom.loadXML(content);
      }
      catch (e2) {
        xmlDom = (new DOMParser()).parseFromString(content, 'application/xml');
      }
    }

    return xmlDom;
  }

http://home.arcor.de/martin.honnen/javascript/2016/test2016022301.html 中所做的那样。