Javascript中的XML
/* ******************* 以下为未完成的原文,有些地方有误。保留。 ****************** */
在IE7/8中,有了所谓的原生(Native)的XMLHttpRequest。使用该类在IE中创建的对象,与使用ActiveXObject创建的xhr对象相比,前者几乎继承了后者的全部缺点。而且,也许是出于安全方面的考虑,用户不能对该对象属性进行枚举,这点与AXO对象相同,所以用户无法查看其可用的属性和方法;此外,不知道为什么,IE原生XHR对象返回的responseXML,在查看其documentElement的typeof时,返回unknown,但是直接alert(responseXML.documentElement)时,却会显示为null,如果调用selectNodes等方法,会报错提示documentElement为null。而axo创建的xhr,无论IE版本如何,都可以直接使用documentElement作为一个xmlDoc,进行xpath查询等各项操作。另外,提到unknown类型,这个IE的特色,xhr的readyState为1/2时,status也是unknown,但是这个属性除了使用typeof外,不能对它进行任何操作,包括值比较。只有在readyState为3/4的时候,status会成为一个number,才能参与运算。xhr的onreadystatechange,在赋值前和赋值后均为unknown。很纠结。
在xhr返回4/200后,我们非常习惯使用responseText。如果有查询方面的需求,一般我们会写一个通用的parseDom方法,将文本转换为dom来处理。对responseXML的使用案例非常之少,就算偶尔用到一些,也往往是基于IE平台,甚至基于本地文件来操作,对ajax方式返回的responseXML几乎没有人去谈及。我想这里有几个原因:一是标准差别迥异,实现通用方法很难;二是JSON的出现,基于js原生对象,不但数据更少,而且可以查询;三是对xml本身的陌生,xml苛刻和繁琐的标准阻碍了它的普及。
我对responseXML一直念念不忘的原因,是因为在JSON出现之前,我已经开始采用AJAX + WebService的方式来实现一些需求。WebServiece返回的是标准的XML文档,而且使用.NET平台创建WebService非常容易。但是返回的结果,一个XML文档,如果我使用responseText的话,其实没有任何意义,至少是偏离了这个模式的本意。由于没有找到解决xml跨平台的办法和较好的案例,所以我一直让WebService返回一个类似JSON的文本,客户端用js通过正则提取JSON文本,然后转换为本地对象的方式来进行交互。(未完)
/* ****************************** 原文保留结束 *********************************** */
上面的话中,对IE7/8原生XMLHttpRequest的一些看法有些问题。但是我依然保留我的一个观点,亦即:这个原生的XHR继承了ActiveXObject的XHR的几乎全部缺点。但是出于对AXO将来命运的担忧,我还是建议在IE中使用原生XHR类来创建对象。
其实大概在上文发出后的两三天里,我就找到了IE原生xhr无法正确返回xml对象的原因。问题出在我的WebService + Ajax模式,IE7/8的xhr对象只是无法正确接收soap1.2的xml文档,所以responseXML是null,即使使用AXO创建的xhr对象也存在同样问题。但让人搞不懂的是,IE6,包括ieTester/IE6,都可以正常接收soap1.2。查不到资料。我只能认为,IE7/8更标准了,它们是在返回一个真正的xml,而不是一个标准宽松的dom。
soap1.1/1.2的文本内容,无论是发送的参数还是返回的结果,仅在一个命名空间的不同,其他完全一样。
所以,当使用soap1.1进行通信时,一切都很正常。IE8的xhr返回了一个运行良好的xmlDocument。至于命名空间对查询的约束……几乎没有,用起来还是那么像dom。当然,你可以使用xpath查询……好吧,正像很多人所说的,其实不一定非得用xml,json多好啊……无所谓,我解决了自己的纠结而已。
在FireFox下的xml查询是个很麻烦但是思路很清楚的事情,只要掌握了其中的思路,很快就可以做出简化查询的通用方法。问题的关键就是对命名空间处理的Namespace Resolver委托函数;而这里让我纠结了半天的并不是如何去写一个恰当的委托函数,而是对“直接量”的命名空间如何处理。解决的办法是在FF官网找到的,找到之后我有些哭笑不得:这个委托函数实际就是做一个枚举,比如<nodeName:ns>这样的节点,委托函数是用来告诉查询器,后面的ns对应哪一个命名空间地址。但是<nodeName xmlns="http://www.xxx.com">的ns是什么呢?看一下代码吧:
2 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
3 <soap:Body>
4 <getSupportCityResponse xmlns="http://WebXml.com.cn/">
5 <getSupportCityResult>
6 <string>string</string>
7 <string>string</string>
8 </getSupportCityResult>
9 </getSupportCityResponse>
10 </soap:Body>
11 </soap:Envelope>
12 <!-- 借用地址 http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?op=getSupportCity -->
相应的Resolver函数应该类似于:
switch(prefix){
case 'post'
: return 'http://www.w3.org/2003/05/soap-envelope';
case 'xsi'
: return 'http://www.w3.org/2001/XMLSchema-instance';
case 'xsd'
: return 'http://www.w3.org/2001/XMLSchema';
}
return 'http://WebXml.com.cn/';
}
然后我把它写成了:
return {
post : 'http://www.w3.org/2003/05/soap-envelope',
xsi : 'http://www.w3.org/2001/XMLSchema-instance',
xsd : 'http://www.w3.org/2001/XMLSchema'
}[prefix] || 'http://WebXml.com.cn/';
}
于是有:
var result = null
, evaluator = new XPathEvaluator()
, _result = evaluator.evaluate(xpath
, this
, nsResolver
, XPathResult.ORDERED_NODE_ITERATOR_TYPE
, null)
, ele;
if(_result){
result = [];
while(ele = _result.iterateNext())
result.push(ele);
}
_result = ele = null;
return result;
}
在线写很累很累,就写到这里吧。
(我认为依然未完)
PS1:gnome-editor就是个很好的js编辑器,还支持着色。
PS2:mono开发工具稳定性和调试工具比vs实在差很多,被折磨了一个星期了,依然无法适应。
PS3:不得不承认VS2010很强大,使得WPF非常容易。加之我的coding能力和超凡的悟性,作为第一次SilverLight,中午就把SL上传基本搞定,所以纠结两件事情:1、是否放弃js类库开发。2、是否回到windows平台。3、虽然我以前很痛恨flash,为什么我对sl一点都不排斥呢?
PS4:其实今天我是来打酱油的。