如何在jQuery中解析XML跨域?
从其他服务器/域解析XML时,如何解决跨域问题? 有人可以给我一个例子吗? 该示例不必仅限于jQuery,因为JavaScript也足够了。
要完全理解为什么纯跨域XML不起作用,首先要了解如何促进跨域JSON。
首先,让我们看看在jQuery中发出AJAX请求时会发生什么:
$.ajax({ url: '/user.php?userId=123', success: function(data) { alert(data); // alerts the response });
在上面的示例中,AJAX请求是相对于域进行的。 我们知道,如果我们尝试在路径之前添加不同的域,则请求将失败并出现安全性exception。
但是,这并不是说浏览器无法向另一个域发出请求。 以下是您可能熟悉的示例:
基于我们如何在页面上导入JavaScript的知识,我们发现可以加载另一个域上存在的资源!
JSONP是一个利用这些知识的概念。 JSONP代表“带填充的JSON”,它的成功取决于JavaScript对象可以使用字符串表示法表达,以及JavaScript脚本标记可以从外部域加载和运行内容这一事实。
在引擎盖下,jQuery的JSONP看起来像这样,虽然它可能不完全:
// programmatically load a script tag on the page using the given url function loadRemoteData(url) { var script = document.createElement("script"); script.setAttribute("type","text/javascript"); script.setAttribute("src", url); document.getElementsByTagName("head")[0].appendChild(script); }
此外,在某个页面上,我们定义了一个回调处理程序:
function processData(jsonResult) { alert(JSON.stringify(jsonResult)); //alert the JSON as a string }
在这里,我们提出要求:
// make a request for the data using the script tag remoting approach. loadRemoteData("http://example.com/users.php?userId=123&callback=processData");
为了使其正常工作,我们的PHP脚本必须以JSON格式返回数据,并且还必须以JavaScript函数名称的forms在字符串周围添加“填充”,我们可以将其作为参数传递(即“回调”) )
因此,如果我们在Firebug或Chrome NET选项卡中查看它,服务器的响应可能看起来像这样:
processData( { "userId" : "123" , "name" : "James" , "email" : "example@example.com" } );
因为我们知道JavaScript内容在下载后立即运行,所以我们之前定义的processData
函数会立即被调用,并将我们的JSON字符串作为参数传递。 然后使用JSON.stringify将对象转换回字符串。
既然它是一个对象,我也可以访问它的属性,如下所示:
function processData(jsonResult) { alert(JSON.stringify(jsonResult)); //alert the JSON as a string // alert the name and email alert("User name is " + jsonResult.name + " and email is " + jsonResult.email); }
最后,让我们转到主要问题: JSONP可以用于获取XML,还是我们可以解析XML跨域? 正如其他人所指出的那样,答案是一个响亮的NO,但让我们通过一个例子看看为什么:
processData(12345 James example@example.com );
现在,如果将原始XML传递给函数会发生什么? 它会破解,因为JavaScript无法将XML转换为JSON。
但是,假设我们将XML放在引号中:
processData("12345 James example@example.com ");
现在,在这个例子中,jsonResult变量实际上是一个字符串,我们可以使用它。 使用一些JavaScript XML解析实用程序,我们可以将该字符串加载到XML DOM Parser中并使用它来完成任务!
但是,它不是纯粹的XML,它仍然是一个引擎盖下的JavaScript响应。 PHP服务器的响应类型仍然是text / javascript,我们仍然使用脚本标记来加载纯粹的JavaScript。
总而言之,您可以使用“XMLP”或带填充的XML(我只是这样做,它不是真的!),但如果您要经历所有实际修改响应以返回函数回调的麻烦包装器,您也可以将输出转换为JSON,让浏览器自动和本地处理转换,并省去必须使用XML解析器的麻烦。
但是,如果由于某种原因将数据保存为XML格式更容易,您可以修改响应并为其提供JavaScript包装器。
我可以看到这种情况有用的情况可能是,如果您从存储在数据库中的遗留应用程序中获取XML数据,并使用脚本标记远程处理或JSONP调用将其返回到客户端。
我发现了一个非常好的解决方案,可以从跨域ajax请求中检索xml。
从jQuery 1.5开始,您可以使用dataType“jsonp xml”(http://api.jquery.com/jQuery.ajax/)!
所以我用过这个:
$.ajax({ type: "GET", url: "http://yoururl", dataType: "jsonp xml", success: function(xmlResponse) { // process data } });
我的Webservices的服务器端用于将xml字符串结果封装在jQuery创建的回调中:
private static Stream GetXmlPStream(string result, string callback) { if (result == null) result = string.Empty; result = EncodeJsString(result); if (!String.IsNullOrEmpty(callback)) result = callback + "(" + result + ");"; byte[] resultBytes = Encoding.UTF8.GetBytes(result); if (WebOperationContext.Current != null) WebOperationContext.Current.OutgoingResponse.ContentType = "application/xml"; return new MemoryStream(resultBytes); }
和魔术方法(我发现它在另一个堆栈线程中)你需要清理你的xml字符串(所以javascript可以解析它):
private static string EncodeJsString(string s) { StringBuilder sb = new StringBuilder(); sb.Append("\""); foreach (char c in s) { switch (c) { case '\"': sb.Append("\\\""); break; case '\\': sb.Append("\\\\"); break; case '\b': sb.Append("\\b"); break; case '\f': sb.Append("\\f"); break; case '\n': sb.Append("\\n"); break; case '\r': sb.Append("\\r"); break; case '\t': sb.Append("\\t"); break; default: int i = (int)c; if (i < 32 || i > 127) { sb.AppendFormat("\\u{0:X04}", i); } else { sb.Append(c); } break; } } sb.Append("\""); return sb.ToString(); }
希望这会有所帮助!
我意识到这是一个老问题,但我在搜索时发现了这个问题。 而且,我认为答案是针对与此处发布的问题略有不同的问题,所以我想添加这个应该至少在jQuery 1.12及更高版本中应用的答案。 我没有在早期版本中测试过。
好的,我想请求此URL: http://sample.domain/feeds/itemdata.xml
我想找到这样的Item
,如下所示:
-
1228101530 ...
这是有效的,跨域:
$.ajax({ dataType: "xml", url: "http://sample.domain/feeds/itemdata.xml", success: function(xml) { var itemdata = $(xml).find("ProductItemNo:contains('1228101530')").parent(); } });