JavaScript中的XML
浏览器中的XML DOM支持
1、IE中的XML DOM 支持
在为IE添加XML支持时,微软在JavaScript之外另寻了个方案:基于ActiveX的MSXML库。
1)、DOM创建
如果尝创建不存在于客户端机器上的ActiveX对象,IE就会抛出错误并停止所有执行。为确保使用了正确的XML DOM版本,也为避免任何其他错误:
<script type="text/javascript">
function createXMLDOM() {
var arrSignatures = ["MSXML2.DOMDocument.5.0", "MSXML2.DOMDocument.4.0", "MSXML2.DOMDocument.3.0", "MSXML2.DOMDocument", "Microsoft.XmlDom"];
for (var i = 0; i < arrSignatures.length; i++) {
try {
var oXmlDom = new ActiveXObject(arrSignatures[i]);
return oXmlDom;
}
catch (oError) {
}
}
throw new Error("MSXML is not installed on your system.");
}
</script>
arrSingatures数组,包含了所有可能的XML DOM字符串,且按照最新到最旧降序排列。
2)、载入XML
微软的XML DOM有两种载入XML的方法:loadXML()和load().
loadXML()方法可直接向XML DOM输入XML字符串:
oXmlDom.loadXML("<root><child/></root>");
load()方法用于从服务器上载入XML文件。
还有两种载入文件的模式:同步和异步。以同步模式载入文件时,JavaScript代码会等待文件完全载入后才继续执行代码;而以异步模式载入时,不会等待,可以使用事件处理函数来判断文件是否完全载入了。默认情况下,文件按照异步模式载入。要进行同步载入,只需设置async特性为false:
oXmlDom.async = false;
然后使用load()方法,并给出要载入的文件名:
oXmlDom.load("test.xml");
执行这一行代码后,oXmlDom会包含能表示XML文件结构的一个DOM文档,这样就可以使用DOM所有的特性和方法了:
alert("Tag name of the root emement is " + oXmlDom.documentElement.tagName);
alert("The root emement has this many children: " + oXmlDOm.documentElement.childNodes.length);
异步载入文件时,要使用readyState特性和onreadystatechange事件处理函数:
readyState特性有五种可能的值:
0:DOM尚未初始化任何信息;
1:DOM正在载入数据;
2:DOM完成了数据载入;
3:DOM已经可用,不过某些部分可能还不能用;
4:DOM已经完全被载入,可以使用了。
必须在调用load()方法前分配好onreadystatechange事件处理函数,就像下面代码中那样:
oXmlDom.onreadystatechange = function()
{
if(oXmlDom.readyState == 4)
{
alert("Done");
}
};
oXmlDom.load("test.xml");
3、获取XML
获取载入后的XML:
oXmlDom.load("test.xml");
alert(oXmlDom.xml);
也可以仅获取某个特定节点的XML:
var oNode = oXmlDom.documentElements.childNodes[1];
alert(oNode.xml);
xml特性是只读的,如果尝试直接对其赋值会产生错误。
4、解释错误
parseError特性实际上是包含以下特性的对象:
errorCode:表示所发生的错误类型的数字代号(当没有错误时为0);
filePos:错误发生有文件中的位置;
line:遇到错误的行号;
linepos:在遇到错误的那一行上的字符的位置;
reason:对错误的一个解释;
srcText:造成错误的代码;
url:造成错误的文件的URL。
2、Mozilla中XML DOM支持
1)、创建DOM
DOM标准指出,document.implementation对象有个可用的createDocument()方法。Mozilla严格遵循了这个标准,可以这样创建XML DOM:
var oXmlDom = document.implementation.createDocument("","",null);
createDocument()的三个参数分别是文档的命名空间URL,文档元素的标签以及一个文档类型对象(总是为null,因为在Mozilla中还没有对文档类型对象的支持)。
2)、载入XML
Mozilla只支持一个载入数据的方法:load().
Mozilla的XML DOM会在文件完全载入后触发load事件,也就是说必须使用onload事件处理函数来判断DOM何时可用:
oXmlDom.onload = function()
{
alert("Done");
}
oXmlDom.load("test.xml");
Mozilla的XML DOM不支持loadXML()方法。要将XML字符串解析为DOM,必须使用DOMParser对象:
var oParser = new DOMParser();
var oXmlDom = oParser.parseFromString("<root/>","text/xml");
可以自己的添加loadXML()方法,XML DOM实际的类称为Document,所以添加新方法同使用prototype对象一样容易,然后用DOMParser创建新的XML DOM,原来的文档必须清空其内容,可用while循环来删除所有的文档的子节点,删除所在的子节点后,所有的oXmlDom的子节点必须导入到文档中(使用importNode()方法)并作为子节点添加(使用appendChild()):
Document.prototype.loadXML = function(sXML)
{
var oParser = new DOMParser();
var oXmlDom = oParser.parseFromString(sXml,"text/xml");
while(this.firstChild)
{
this.removeChild(this.firstChild);
}
for(var i = 0;i < oXmlDom.childNodes.length;i++)
{
var oNewNode = this.importNode(oXmlDom.childNodes[i],true);
this.appendChild(oNewNode);
}
};
3)、获取XML
Mozilla提供了XMLSerializer对象:
var oSerializer = new XMLSerializer();
var sXml = oSerializer.serializeToString(oXmlDom,"text/xml");
serializeToString()为oXmlDom创建了XML代码,它接受要进行序列化的节点和内容类型作为参数。
defineGetter()方法只存在于Mozilla中,为某个特性定义获取函数,也就是说,读取特性时,就会调用这个函数并返回它的结果。例:
var sValue = oXmlNode.nodeValue; //读
oXmlNode.nodeValue = "New value"; //写
defineGetter()需要两个对数:特性的名称和要调用的函数。
4)、解析错误
要判断是否在XML代码的解析过程中有错误,必须测试文档元素的标签名:
if(oXmlDom.documentElement.tagName != "parsererror")
{
}
else
{
}
唯一能获取确切的错误信息的方法是,解析错误信息文本。最简单的方法是使用正则表达式:var reError = />([\s\s]*?)Location:([\s\s]*?)Line Number (\d+),Column(\d+):<sourcetext>([\s\s*?)(?:\-*\^)/;
这段正是表达式抓取了所有XML代码中潜在的信息。第一个捕获性分组获取错误信息,第二个获取文件名,第三个获取行号,第四个获取列号,第五个获取造成错误的源代码(不包含最后的横线和脱字符号)。
3、通用接口
1)、修改DOM创建
第一步是创建IE和Mozilla通用的创建XML DOM对象的方法。最简单的方法是创建伪类,使之可以这样创建XML DOM:
var oXmlDom = new XmlDom();
当然,在XmlDom()构造函数中必须进行浏览器检测:
function XmlDom()
{
if(window.ActiveXObject)
{
}
else if(document.implementation && document.implementation.createDocument)
{}
else
{
throw new Error("Your browser doesn't support an XML DOM object.");
}
}
2)、IE分支
对于构造函数的IE部分,只需把createXMLDOM()函数中的代码插进来即可。
3)、Mozilla分支
Mozilla分支的第一步是,用createDocument()方法创建XML DOM对象。再让Mozilla支持readyState特性以及onreadystatechange事件处理函数。
首先,添加readyState特性,并初始化为0;
Document.prototype.readyState = 0;
下面,创建onreadystatechange特性,并初始化为null;
Document.prototype.onreadystatechange = null;
一旦readyState特性发生变化,必须调用onreadystatechange函数。为达到这个目的,最好创建一个方法:
Document.prototype.__changeReadyState__ = function(iReadyState)
{
this.readyState = iReadyState;
if(typeof this.onreadystatechange == "runction")
{
this.onreadystatechange();
}
};
影响到readyState特性的两个方法是,loadXML()和load()。
4)、错误处理
最后一步是为Mozilla创建parseError对象。
下面是完整代码:
function XmlDom() {
if (window.ActiveXObject) {
var arrSignatures = ["MSXML2.DOMDocument.5.0", "MSXML2.DOMDocument.4.0",
"MSXML2.DOMDocument.3.0", "MSXML2.DOMDocument",
"Microsoft.XmlDom"];
for (var i=0; i < arrSignatures.length; i++) {
try {
var oXmlDom = new ActiveXObject(arrSignatures[i]);
return oXmlDom;
} catch (oError) {
//ignore
}
}
throw new Error("MSXML is not installed on your system.");
} else if (document.implementation && document.implementation.createDocument) {
var oXmlDom = document.implementation.createDocument("","",null);
oXmlDom.parseError = {
valueOf: function () { return this.errorCode; },
toString: function () { return this.errorCode.toString() }
};
oXmlDom.__initError__();
oXmlDom.addEventListener("load", function () {
this.__checkForErrors__();
this.__changeReadyState__(4);
}, false);
return oXmlDom;
} else {
throw new Error("Your browser doesn't support an XML DOM object.");
}
}
if (isMoz) {
Document.prototype.readyState = 0;
Document.prototype.onreadystatechange = null;
Document.prototype.__changeReadyState__ = function (iReadyState) {
this.readyState = iReadyState;
if (typeof this.onreadystatechange == "function") {
this.onreadystatechange();
}
};
Document.prototype.__initError__ = function () {
this.parseError.errorCode = 0;
this.parseError.filepos = -1;
this.parseError.line = -1;
this.parseError.linepos = -1;
this.parseError.reason = null;
this.parseError.srcText = null;
this.parseError.url = null;
};
Document.prototype.__checkForErrors__ = function () {
if (this.documentElement.tagName == "parsererror") {
var reError = />([\s\S]*?)Location:([\s\S]*?)Line Number (\d+), Column (\d+):<sourcetext>([\s\S]*?)(?:\-*\^)/;
reError.test(this.xml);
this.parseError.errorCode = -999999;
this.parseError.reason = RegExp.$1;
this.parseError.url = RegExp.$2;
this.parseError.line = parseInt(RegExp.$3);
this.parseError.linepos = parseInt(RegExp.$4);
this.parseError.srcText = RegExp.$5;
}
};
Document.prototype.loadXML = function (sXml) {
this.__initError__();
this.__changeReadyState__(1);
var oParser = new DOMParser();
var oXmlDom = oParser.parseFromString(sXml, "text/xml");
while (this.firstChild) {
this.removeChild(this.firstChild);
}
for (var i=0; i < oXmlDom.childNodes.length; i++) {
var oNewNode = this.importNode(oXmlDom.childNodes[i], true);
this.appendChild(oNewNode);
}
this.__checkForErrors__();
this.__changeReadyState__(4);
};
Document.prototype.__load__ = Document.prototype.load;
Document.prototype.load = function (sURL) {
this.__initError__();
this.__changeReadyState__(1);
this.__load__(sURL);
};
Node.prototype.__defineGetter__("xml", function () {
var oSerializer = new XMLSerializer();
return oSerializer.serializeToString(this, "text/xml");
});
}