傳送與接收 XML



如果你需要用非同步物件傳送複雜階層的資料,可以使用XML。如果你需要傳送XML,只需將資料組織為XML的字串,open()時使用'POST',並設定請求標頭的'Content-Type'為'text/xml',再使用send()方法將XML字串放在本體中傳送出去。例如:
function toXML(data) {
    var xml = ['<data>'];
    for(var name in data) {
        xml.push('<' + name + '>');
        xml.push(data[name]);
        xml.push('</' + name + '>');
    }
    xml.push('</data>');
    return xml.join('');
}

...
var data = { // 假設 data 實際上是由使用者提供
    x : 10,
    y : 20,
    z : 30
};
var request = xhr(); // xhr() 會建立非同步物件
request.onreadystatechange = handleStateChange; // handleStateChange 參考至函式
request.open('POST', url);
request.setRequestHeader('Content-Type', 'text/xml');
request.send(toXML(data));

上面這個例子,伺服端會收到的XML字串為(底下是加了一些排版的結果):
<data>
    <x>10</x>
   
<y>20</y>
    <z>30</z>
</data>


當然,伺服端必須剖析XML字串,取出所要的資料進行處理,這邊不討論伺服端如何剖析XML字串。

如果伺服端傳回XML字串,則可以使用非同步物件的respnseXML取得XML字串剖析後的DOM物件,之後使用DOM API取出想要的資料進行處理。例如在 使用 GET 請求 的第一個範例中,使用innerHTML將傳回的HTML字串設定為<div>的內部HTML,但這樣就是伺服端寫死了客戶端的HTML外觀,你可以改傳回XML,由客戶端取出資料,自行決定外觀處理。

例如,若伺服端傳回的XML格式如下(注意,伺服端必須設定回應標頭的"Content-Type"為"text/xml",XML若含中文之類的字元,則必須指定charset編碼):
<?xml version="1.0" encoding="UTF-8"?>
<select>
    <option value="algorithm">常見演算</option>
    <option value="graphic">電腦圖學</option>
    <option value="pattern">設計模式</option>
</select>

以下是改寫 使用 GET 請求 的第一個範例,處理傳回的XML:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN">
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type">
<script type="text/javascript">
window.onload = function() {
var xhr = window.XMLHttpRequest &&
(window.location.protocol !== 'file:'
|| !window.ActiveXObject) ?
function() {
return new XMLHttpRequest();
} :
function() {
try {
return new ActiveXObject('Microsoft.XMLHTTP');
} catch(e) {
throw new Error('XMLHttpRequest not supported');
}
};

function param(obj) {
var pairs = [];
for(var name in obj) {
var pair = encodeURIComponent(name) + '=' +
encodeURIComponent(obj[name]);
pairs.push(pair.replace('/%20/g', '+'));
}
return pairs.join('&');
}

function ajax(option) {
option.type = option.type || 'GET';
option.header = option.header || {
'Content-Type':'application/x-www-form-urlencoded'};
option.callback = option.callback || function() {};

if(!option.url) {
return;
}

var request = xhr();
request.onreadystatechange = function() {
option.callback.call(request, request);
};

var body = null;
var url = option.url;
if(option.data) {
if(option.type === 'POST') {
body = param(option.data);
}
else {
url = option.url + '?' + param(option.data)
+ '&time=' + new Date().getTime();
}
}

request.open(option.type, url);
for(var name in option.header) {
request.setRequestHeader(
name, option.header[name]);
}
request.send(body);
}

document.getElementById('category').onchange = function() {
ajax({
url : 'XML-1.php',
data : {category : this.value},
callback: function(request) {
if(request.readyState === 4) {
if(request.status === 200) {
var select =
document.createElement('select');
var xml = request.responseXML;
// 取得所有 <option>
var options =

xml.getElementsByTagName('option');
for(var i = 0; i < options.length; i++) {
// 取得每個 <option> 的 value 屬性
var value =
options[i].getAttribute('value');

// 取得每個 <option></option> 間的文字
// 注意,文字也是節點
var text =
options[i].firstChild.nodeValue;

// 不是 IE 的話
if(navigator.userAgent
.indexOf('MSIE') === -1) {
select.add(
new Option(text, value),
select.options[
select.options.length]);
}
else { // IE 的話
select.add(
new Option(text, value),
select.options.length);
}
}
var book =
document.getElementById('book');
if(book.firstChild) {
book.removeChild(book.firstChild);
}
book.appendChild(select);
}
}
}
});
};
};
</script>
</head>
<body>
圖書:<br>
<select id="category">
<option>-- 選擇分類 --</option>
<option value="theory">理論基礎</option>
<option value="language">程式語言</option>
<option value="web">網頁技術</option>
</select><br><br>
採購:<div id="book"></div>
</body>
</html>

上例的執行結果與 使用 GET 請求 雖然一樣,但實際上你可以用任意畫面展現傳回的XML資料。

上例中並示範了下拉選單的選項在新增時,標準瀏覽器與Internet Explorer不同的處理方式,在這邊無法使用物件偵測來判斷,因為新增下拉的選項時,標準瀏覽器與Internet Explorer都是使用add()方法,差別僅在於第二個參數,標準瀏覽器必須指定已存在的選項,新增的選項會安插在該選項之前,而Internet Explorer則必須指定已存在選項的索引位置,新增的選項會安插在該選項之前。

優先使用物件偵測,只在無法使用物件偵測時,才使用這邊的瀏覽器探測方式。