傳送與接收 XML


如果需要用非同步物件傳送複雜階層的資料,可以使用 XML。如果需要傳送 XML,只需將資料組織為 XML 的字串,open 時使用 'POST',並設定請求標頭的 'Content-Type''text/xml',再使用 send 方法將 XML 字串放在本體中傳送出去。例如:

function toXML(data) {
    let xml = Object.keys(data)
                    .map(name => `<${name}>${data[name]}</${name}>`)
                    .join('');
    return `<data>${xml}</data>`;
}

...
let data = { // 假設 data 實際上是由使用者提供
    x : 10,
    y : 20,
    z : 30
};

var request = new XMLHttpRequest();
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 字串,可以使用非同步物件的 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,為了便於兩個範例對照,暫不使用之前看過的對 XMLHttpRequest 之簡單封裝:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
</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>

<script type="text/javascript">    

    document.getElementById('category').onchange = function(evt) {
        let request = new XMLHttpRequest();

        request.onload = function(evt) {
            let req = evt.target;
            if(req.status === 200) {
                let select = document.createElement('select');
                let xml = request.responseXML;

                // 取得所有 <option>
                let options = xml.getElementsByTagName('option');
                Array.from(options).forEach(option => {
                    // 取得每個 <option> 的 value 屬性
                    let value = option.getAttribute('value');
                    // 取得每個 <option></option> 間的文字
                    // 注意,文字也是節點
                    let text = option.firstChild.nodeValue;
                    select.add(
                        new Option(text, value), 
                        select.options[select.options.length]
                    );         
                });


                let book = document.getElementById('book');
                if(book.firstChild) {
                    book.removeChild(book.firstChild);
                }
                book.appendChild(select);

            }
        };

        let time = new Date().getTime();
        let url = `XML-1.php?category=${evt.target.value}&time=${time}`;
        request.open('GET', url);
        request.send(null);
    };
</script>

</body>
</html>

按我觀看執行結果

上例的執行結果與〈使用 GET 請求〉的第一個範例,處理傳回的 XML,為了便於兩個範例對照,暫不使用之前看過的對 XMLHttpRequest 之簡單封裝雖然一樣,但實際上你可以用任意畫面展現傳回的 XML 資料。