AJAX XML
Serving XML, parsing it client-side, and why JSON won.
Key PointsXML is verbose but supports attributes, namespaces and schema validation.
Serving XML
PHP
<?php
// /api/users.xml.php
header("Content-Type: application/xml; charset=utf-8");
$doc = new DOMDocument("1.0", "UTF-8");
$doc->formatOutput = true;
$root = $doc->createElement("users");
$doc->appendChild($root);
foreach ([["1", "Tom", "tom@example.com"], ["2", "Amy", "amy@example.com"]] as [$id, $name, $email]) {
$u = $doc->createElement("user");
$u->setAttribute("id", $id);
$u->appendChild($doc->createElement("name", $name));
$u->appendChild($doc->createElement("email", $email));
$root->appendChild($u);
}
echo $doc->saveXML();Output
<?xml version="1.0" encoding="UTF-8"?>
<users>
<user id="1">
<name>Tom</name>
<email>tom@example.com</email>
</user>
<user id="2">
<name>Amy</name>
<email>amy@example.com</email>
</user>
</users>Parsing on the client
JavaScript
async function loadUsers() {
const res = await fetch("/api/users.xml.php");
const text = await res.text();
const doc = new DOMParser().parseFromString(text, "application/xml");
const err = doc.querySelector("parsererror");
if (err) throw new Error("XML 解析失败: " + err.textContent);
const users = [...doc.querySelectorAll("user")].map(u => ({
id: u.getAttribute("id"),
name: u.querySelector("name").textContent,
email: u.querySelector("email").textContent,
}));
console.log(users);
}The parsed structure:
Output
[{ id: "1", name: "Tom", email: "tom@example.com" },
{ id: "2", name: "Amy", email: "amy@example.com" }]DOMParser does not throw on failure; it produces a document containing <parsererror>. Skip that check and you get a confusing null-property error later.XML vs JSON
| Aspect | XML | JSON |
|---|---|---|
| Verbosity | 高,成对标签 | 低 |
| Attributes | 支持 | 不支持,只能嵌套 |
| Namespaces | 支持 | 不支持 |
| Schema | XSD / DTD | JSON Schema |
| Native in JS | 需要 DOMParser | JSON.parse |
| Binary | 需 Base64 | 需 Base64 |
| Typical use | SOAP、RSS、Office 文档、企业集成 | REST API、配置文件 |
Supporting both
Serve different formats based on the Accept header — content negotiation.
PHP
<?php
$items = [
["id" => 1, "name" => "Tom"],
["id" => 2, "name" => "Amy"],
];
$accept = $_SERVER["HTTP_ACCEPT"] ?? "application/json";
if (str_contains($accept, "application/xml")) {
header("Content-Type: application/xml; charset=utf-8");
$doc = new DOMDocument("1.0", "UTF-8");
$root = $doc->createElement("items");
$doc->appendChild($root);
foreach ($items as $it) {
$n = $doc->createElement("item");
$n->setAttribute("id", (string)$it["id"]);
$n->appendChild($doc->createElement("name", $it["name"]));
$root->appendChild($n);
}
echo $doc->saveXML();
} else {
header("Content-Type: application/json; charset=utf-8");
echo json_encode($items, JSON_UNESCAPED_UNICODE);
}Output
<?xml version="1.0" encoding="UTF-8"?>
<items><item id="1"><name>Tom</name></item><item id="2"><name>Amy</name></item></items>New endpoints should return JSON by default. XML is only for legacy integrations — SOAP, banking, government systems.
返回 XML
PHP
<?php
// /api/users.xml.php
header("Content-Type: application/xml; charset=utf-8");
$doc = new DOMDocument("1.0", "UTF-8");
$doc->formatOutput = true;
$root = $doc->createElement("users");
$doc->appendChild($root);
foreach ([["1", "Tom", "tom@example.com"], ["2", "Amy", "amy@example.com"]] as [$id, $name, $email]) {
$u = $doc->createElement("user");
$u->setAttribute("id", $id);
$u->appendChild($doc->createElement("name", $name));
$u->appendChild($doc->createElement("email", $email));
$root->appendChild($u);
}
echo $doc->saveXML();Output
<?xml version="1.0" encoding="UTF-8"?>
<users>
<user id="1">
<name>Tom</name>
<email>tom@example.com</email>
</user>
<user id="2">
<name>Amy</name>
<email>amy@example.com</email>
</user>
</users>前端解析
JavaScript
async function loadUsers() {
const res = await fetch("/api/users.xml.php");
const text = await res.text();
const doc = new DOMParser().parseFromString(text, "application/xml");
const err = doc.querySelector("parsererror");
if (err) throw new Error("XML 解析失败: " + err.textContent);
const users = [...doc.querySelectorAll("user")].map(u => ({
id: u.getAttribute("id"),
name: u.querySelector("name").textContent,
email: u.querySelector("email").textContent,
}));
console.log(users);
}浏览器解析出的结构:
Output
[{ id: "1", name: "Tom", email: "tom@example.com" },
{ id: "2", name: "Amy", email: "amy@example.com" }]DOMParser 解析失败不会抛异常,而是生成一个含 <parsererror> 的文档。不检查它,后面 querySelector("user") 会返回 null 然后报「读取 null 的属性」——错误信息和真实原因完全对不上。XML 与 JSON 对比
| 对比项 | XML | JSON |
|---|---|---|
| 冗长程度 | 高,成对标签 | 低 |
| 属性 | 支持 | 不支持,只能嵌套 |
| 命名空间 | 支持 | 不支持 |
| 结构校验 | XSD / DTD | JSON Schema |
| JS 原生支持 | 需要 DOMParser | JSON.parse |
| 二进制 | 需 Base64 | 需 Base64 |
| 典型场景 | SOAP、RSS、Office 文档、企业集成 | REST API、配置文件 |
同时支持两种格式
接口按 Accept 头返回不同格式,客户端各取所需。这是「内容协商」。
PHP
<?php
$items = [
["id" => 1, "name" => "Tom"],
["id" => 2, "name" => "Amy"],
];
$accept = $_SERVER["HTTP_ACCEPT"] ?? "application/json";
if (str_contains($accept, "application/xml")) {
header("Content-Type: application/xml; charset=utf-8");
$doc = new DOMDocument("1.0", "UTF-8");
$root = $doc->createElement("items");
$doc->appendChild($root);
foreach ($items as $it) {
$n = $doc->createElement("item");
$n->setAttribute("id", (string)$it["id"]);
$n->appendChild($doc->createElement("name", $it["name"]));
$root->appendChild($n);
}
echo $doc->saveXML();
} else {
header("Content-Type: application/json; charset=utf-8");
echo json_encode($items, JSON_UNESCAPED_UNICODE);
}Output
<?xml version="1.0" encoding="UTF-8"?>
<items><item id="1"><name>Tom</name></item><item id="2"><name>Amy</name></item></items>新接口默认就返回 JSON。XML 只在对接老系统(SOAP、银行、政务)时才需要,别主动给自己加负担。