1. 為什麼需要驗證 XML
XML — 是個很彈性的格式,有時候甚至太寬鬆。和嚴格類型化的資料不同,XML 容忍錯誤,如果你忘了關閉一個標籤,或者有人寫錯了屬性名,程式可能在解析時拋錯……或者根本沒注意到問題,這反而更糟。
基於 XSD 的驗證 (XML Schema Definition) — 是一種確保你的 XML 文件完全符合事先描述的合約的方式:哪些標籤叫什麼、它們的順序、類型、是否必填、最多或最少多少個。這有點像給資料辦護照檢查—schema 說:「沒有該欄位或欄位長度不對就別想過來!」
在企業系統、不同系統之間交換資料、API 等場景中,驗證很重要,因為不希望「默默」接受結構不對的資料。
XSD (XML Schema Definition) — 是一個獨立的 XML 文件,用來形式化描述另一個 XML 的結構。在 XSD 裡你可以描述:允許哪些標籤、哪些是必需的、屬性或子元素的類型、字串的模式、數字的限制,甚至可能的值(透過限制和 enumeration)。
| XML | XSD (結構描述) |
|---|---|
|
|
|
|
XSD 在概念上類似於 JSON 世界裡的 JSON Schema。
2. XSD 與對應 XML 範例
XML 範例
<person age="23">
<name>Ivan</name>
<email>ivan@example.com</email>
</person>
XSD 範例
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="person">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="email" type="xs:string" minOccurs="0"/>
</xs:sequence>
<xs:attribute name="age" type="xs:positiveInteger" use="required"/>
</xs:complexType>
</xs:element>
</xs:schema>
這個 schema 描述了:
- 根元素是 <person>。
- 在 <person> 內應該有 <name>(必須)和 <email>(可選)。
- <person> 必須有一個名為 age 的屬性,且值是正整數。
3. 在 .NET 中用 XSD 驗證 XML
在 .NET 中常見做法是使用命名空間 System.Xml 和 System.Xml.Schema。
工作流程:
- 載入 XML 文件
- 載入 XSD schema
- 把 schema 綁到 XML 的驗證上
- 啟動驗證流程並處理可能的錯誤
下面是一個基本範例:
using System;
using System.IO;
using System.Xml;
using System.Xml.Schema;
// 1. 驗證錯誤處理器
void ValidationCallBack(object? sender, ValidationEventArgs e)
{
Console.WriteLine($"驗證錯誤: {e.Message}");
}
string xmlPath = "person.xml";
string xsdPath = "person.xsd";
// 2. 載入 schema
XmlSchemaSet schemas = new XmlSchemaSet();
schemas.Add("", xsdPath);
XmlReaderSettings settings = new XmlReaderSettings();
settings.Schemas = schemas;
settings.ValidationType = ValidationType.Schema;
settings.ValidationEventHandler += ValidationCallBack;
// 3. 以設定打開 XML
using XmlReader reader = XmlReader.Create(xmlPath, settings);
while (reader.Read())
{
// 只遍歷文件 - 驗證是「即時」發生的
}
Console.WriteLine("驗證完成!");
說明:
- 所有驗證錯誤都會在回呼 ValidationCallBack 中被捕捉。
- 如果沒有錯誤 —— 文件符合 schema(ValidationType.Schema)。
- 如果有不符合(例如缺少 age、email 不合法、多了不該有的欄位等)—— 會在控制台看到錯誤。
不同類型的錯誤與反饋
XSD 驗證可能因各種原因報錯。以下是常見情境與錯誤示例:
- 缺少必需元素(預設 minOccurs="1")
- 多餘的標籤,XSD 未描述
- 屬性類型錯誤(例如 age="abc" 而不是數字)
- 字串不符合 pattern(透過 <xs:pattern> 描述)
典型錯誤:
驗證錯誤: 元素 'age' 的文字內容 'abc' 不被允許。預期的類型為 'xs:positiveInteger'。
4. 更複雜的 XSD:類型、enumeration、pattern
XSD 幾乎是一門語言,用來描述允許的資料。以下是幾個進階範例:
允許值的列舉
<xs:element name="gender">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="male"/>
<xs:enumeration value="female"/>
<xs:enumeration value="diverse"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
字串長度限制
<xs:element name="code">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:length value="8"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
email 的 pattern(簡化)
<xs:element name="email">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern value="\w+@\w+\.\w+"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
你可以建立自定義類型、繼承它們、使用巢狀元素 — 幾乎像在做 OOP!
5. 驗證複雜文件
假設我們的教學應用現在匯出學生清單為 XML。來加點程式碼吧!
XML 範例
<students>
<student id="1">
<name>Elena</name>
<grade>5</grade>
</student>
<student id="2">
<name>Alexey</name>
<grade>4</grade>
</student>
</students>
XSD schema
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="students">
<xs:complexType>
<xs:sequence>
<xs:element name="student" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="grade">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:minInclusive value="1"/>
<xs:maxInclusive value="5"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
<xs:attribute name="id" type="xs:integer" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
在程式碼中的驗證(同樣的做法):
// 檔案路徑
string xmlPath = "students.xml";
string xsdPath = "students.xsd";
XmlSchemaSet schemas = new XmlSchemaSet();
schemas.Add("", xsdPath);
XmlReaderSettings settings = new XmlReaderSettings();
settings.Schemas = schemas;
settings.ValidationType = ValidationType.Schema;
settings.ValidationEventHandler += ValidationCallBack;
using XmlReader reader = XmlReader.Create(xmlPath, settings);
while (reader.Read()) { /* 和之前一樣 */ }
6. 有用的小技巧
如何在 XML 中直接指定 schema (xsi:schemaLocation)
實務中你會常看到在根元素直接有 xsi:schemaLocation 或 xsi:noNamespaceSchemaLocation 屬性:
<students xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="students.xsd">
...
</students>
這不會「自動」驗證檔案,但有助於某些 validator 或編輯器找到要用的 schema。在 .NET 的驗證流程中這不是必需的,但從格式上來說是正確的做法。
「即時」驗證與記憶體驗證 — 用 XDocument
如果你用 LINQ to XML(XDocument),也可以在記憶體中驗證 XML,而不一定用檔案:
using System.Xml.Linq;
using System.Xml.Schema;
// 載入 schema
XmlSchemaSet schema = new XmlSchemaSet();
schema.Add("", "students.xsd");
// 在記憶體載入 XML
XDocument doc = XDocument.Load("students.xml");
// 驗證
doc.Validate(schema,
(o, e) => Console.WriteLine($"錯誤: {e.Message}"),
true // 檢查 warning(有時候會過度)
);
這種方式很方便,當你程式化地建立或修改 XML,希望確保沒有生成違反 schema 的「XML 怪物」時特別有用。
7. 使用 XSD 驗證 XML 的常見錯誤
錯誤一:忘了加載 schema 或 XSD 路徑錯了。
結果就是沒有執行任何驗證,XML 文件就這樣「穿過關卡」,即使裡面有問題。經常是路徑用相對路徑,但應用程式在意想不到的位置找檔。測試時最好用絕對路徑或把 schema 放在可執行檔同目錄下。
錯誤二:namespace 不匹配。
如果你的 schema 使用了 namespace,加入 schema 到 XmlSchemaSet.Add 時必須傳入那個 namespace 作為第一或第二個參數(依 API 方式)。否則即便 XML 結構正確,驗證也會失敗。如果沒有 namespace,傳空字串。
錯誤三:元素順序不一致。
如果 schema 明確使用了 <xs:sequence>,那元素的順序就重要。元素順序錯了的 XML 會被視為不合法。
錯誤四:XML 指定了多個 namespace。
如果文件使用多個 namespace,可能導致驗證行為意外。支援多個 namespace 需要特別處理與精確的 schema 配置。
錯誤五:不收集或不保存錯誤資訊。
把錯誤直接輸出到控制台適合除錯,但不適合生產環境。更好的做法是把錯誤收集到清單,並以適合使用者的格式回傳——特別是大量檔案驗證(例如在 web 伺服器上)。
GO TO FULL VERSION