CodeGym /課程 /C# SELF /使用 XSD 來驗證

使用 XSD 來驗證 XML

C# SELF
等級 48, 課堂 4
開放

1. 為什麼需要驗證 XML

XML — 是個很彈性的格式,有時候甚至太寬鬆。和嚴格類型化的資料不同,XML 容忍錯誤,如果你忘了關閉一個標籤,或者有人寫錯了屬性名,程式可能在解析時拋錯……或者根本沒注意到問題,這反而更糟。

基於 XSD 的驗證 (XML Schema Definition) — 是一種確保你的 XML 文件完全符合事先描述的合約的方式:哪些標籤叫什麼、它們的順序、類型、是否必填、最多或最少多少個。這有點像給資料辦護照檢查—schema 說:「沒有該欄位或欄位長度不對就別想過來!」

在企業系統、不同系統之間交換資料、API 等場景中,驗證很重要,因為不希望「默默」接受結構不對的資料。

XSD (XML Schema Definition) — 是一個獨立的 XML 文件,用來形式化描述另一個 XML 的結構。在 XSD 裡你可以描述:允許哪些標籤、哪些是必需的、屬性或子元素的類型、字串的模式、數字的限制,甚至可能的值(透過限制和 enumeration)。

XML XSD (結構描述)
<person age="23">Ivan</person>
<element name="person" ... />
<item price="1000"/>
<element name="item" type="..." />

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。

工作流程:

  1. 載入 XML 文件
  2. 載入 XSD schema
  3. 把 schema 綁到 XML 的驗證上
  4. 啟動驗證流程並處理可能的錯誤

下面是一個基本範例:

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 伺服器上)。

2
任務
C# SELF, 等級 48, 課堂 4
上鎖
擴展 XML 結構並進行多規則驗證
擴展 XML 結構並進行多規則驗證
1
問卷/小測驗
處理 XML 資料,等級 48,課堂 4
未開放
處理 XML 資料
設定 XML 序列化
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION