1.1 Đọc và ghi XML mà không phải nạp toàn bộ tàiliệu vào bộ nhớ
V
V
Bạn cần đọcXML từ một stream, hoặc ghi nó ra một stream. Tuy nhiên, bạn
muốn xử lý từng nút một, không phải nạp toàn bộ vào bộ nhớ với một
XmlDocument.
#
#
Để ghi XML, hãy tạo một XmlTextWriter bọc lấy một stream và sử dụng các
phương thức Write (như WriteStartElement và WriteEndElement). Để đọc
XML, hãy tạo một XmlTextReader bọc lấy một stream và gọi phương thức
Read để dịch chuyển từ nút này sang nút khác.
Lớp XmlTextWriter và XmlTextReader đọc/ghi XML trực tiếp từ stream từng nút một.
Các lớp này không cung cấp các tính năng dùng để duyệt và thao tác tàiliệuXML như
XmlDocument, nhưng hiệu năng cao hơn và vết b
ộ nhớ nhỏ hơn, đặc biệt khi bạn làm
việc với các tàiliệuXML cực kỳ lớn.
Để ghi XML ra bất kỳ stream nào, bạn có thể sử dụng XmlTextWriter. Lớp này cung cấp
các phương thức Write dùng để ghi từng nút một, bao gồm:
• WriteStartDocument—ghi phần khởi đầu của tài liệu; và WriteEndDocument, đóng
bất kỳ phần tử nào đang mở ở cuối tài liệu.
• WriteStartElement—ghi một th
ẻ mở (opening tag) cho phần tử bạn chỉ định. Kế đó,
bạn có thể thêm nhiều phần tử lồng bên trong phần tử này, hoặc bạn có thể gọi
WriteEndElement để ghi thẻ đóng (closing tag).
• WriteElementString—ghi một phần tử, cùng với một thẻ mở, một thẻ đóng, và nội
dung text.
• WriteAttributeString—ghi một đặc tính cho phần tử đang mở gần nhất, cùng với tên
và giá trị.
Sử dụng các phương thức này thường cần ít mã lệnh hơn là tạo một XmlDocument bằng
tay, như được trình bày trong mục 5.2 và 5.3.
Để đọc XML, bạn sử dụng phương thức Read của XmlTextReader. Phương thức này tiến
reader đến nút kế tiếp, và trả về true. Nếu không còn nút nào nữa, nó sẽ trả về false. Bạn
có thể thu lấy thông tin về nút hiện tại thông qua các thuộc tính của XmlTextReader (bao
gồm Name, Value, và NodeType).
Để nhậ
n biết một phần tử có các đặc tính hay không, bạn phải kiểm tra thuộc tính
HasAttributes và rồi sử dụng phương thức GetAttribute để thu lấy các đặc tính theo tên
hay theo chỉ số. Lớp XmlTextReader chỉ có thể truy xuất một nút tại một thời điểm, và nó
không thể dịch chuyển ngược hay nhảy sang một nút bất kỳ. Do đó, tính linh hoạt của nó
kém hơn lớp XmlDocument.
Ứng dụng dưới đây ghi và đọc một tài liệuXML bằng lớp XmlTextWriter và
XmlTextReader. Tàiliệu này giống với tàiliệu đã được tạo trong mục 5.2 và 5.3 bằng
lớp XmlDocument.
using System;
using System.Xml;
using System.IO;
using System.Text;
public class ReadWriteXml {
private static void Main() {
// Tạo file và writer.
FileStream fs = new FileStream("products.xml", FileMode.Create);
XmlTextWriter w = new XmlTextWriter(fs, Encoding.UTF8);
// Khởi động tài liệu.
w.WriteStartDocument();
w.WriteStartElement("products");
// Ghi một product.
w.WriteStartElement("product");
w.WriteAttributeString("id", "1001");
w.WriteElementString("productName", "Gourmet Coffee");
w.WriteElementString("productPrice", "0.99");
w.WriteEndElement();
// Ghi một product khác.
w.WriteStartElement("product");
w.WriteAttributeString("id", "1002");
w.WriteElementString("productName", "Blue China Tea Pot");
w.WriteElementString("productPrice", "102.99");
w.WriteEndElement();
// Kết thúc tài liệu.
w.WriteEndElement();
w.WriteEndDocument();
w.Flush();
fs.Close();
Console.WriteLine("Document created. " +
"Press Enter to read the document.");
Console.ReadLine();
fs = new FileStream("products.xml", FileMode.Open);
XmlTextReader r = new XmlTextReader(fs);
// Đọc tất cả các nút.
while (r.Read()) {
if (r.NodeType == XmlNodeType.Element) {
Console.WriteLine();
Console.WriteLine("<" + r.Name + ">");
if (r.HasAttributes) {
for (int i = 0; i < r.AttributeCount; i++) {
Console.WriteLine("\tATTRIBUTE: " +
r.GetAttribute(i));
}
}
}
else if (r.NodeType == XmlNodeType.Text) {
Console.WriteLine("\tVALUE: " + r.Value);
}
}
Console.ReadLine();
}
}
1.2 Xác nhận tính hợp lệ của một tàiliệuXML dựa trên một Schema
V
V
Bạn cần xác nhận tính hợp lệ của một tài liệuXML bằng cách bảo đảm nó tuân
theo một XML Schema.
#
#
Sử dụng lớp System.Xml.XmlValidatingReader. Tạo một thể hiện của lớp này,
nạp Schema vào tập hợp XmlValidatingReader.Schemas, dịch chuyển qua từng
nút một bằng cách gọi XmlValidatingReader.Read, và bắt bất cứ ngoại lệ nào.
Để tìm tất cả các lỗi trong một tàiliệu mà không phải bắt ngoại lệ, hãy thụ lý
sự kiện ValidationEventHandler.
Một XML Schema (giản đồ XML) định nghĩa các quy tắc mà một kiểu tài liệuXML cho
trước phải tuân theo. Các quy tắc này định nghĩa:
• Các phần tử và đặc tính có thể xuất hiện trong tài liệu.
• Các kiểu dữ liệu cho phần tử và đặc tính.
• Cấu trúc củ
a tài liệu, bao gồm các phần tử nào là con của các phần tử khác.
• Thứ tự và số lượng các phần tử con xuất hiện trong tài liệu.
• Các phần tử nào là rỗng, có thể chứa text, hay đòi hỏi các giá trị cố định.
Bàn sâu về các tàiliệuXML Schema vượt quá phạm vi của chương này, nhưng bạn có thể
tìm hiểu nó thông qua một ví dụ đơn giản. Mục này sẽ sử dụng tàiliệuXML mô tả danh
mục sản phẩm đã được trình bày trong mục 5.1.
Ở mức cơ bản nhất, XML Schema Definition (XSD) được sử dụng để định nghĩa các phần
tử có thể xuất hiện trong tàiliệu XML. Bản thân tàiliệu XSD được viết theo dạng XML,
và bạn sử dụng một phần tử đã được định nghĩa trước (có tên là <element>) để chỉ định
các phần tử sẽ cần thiết trong tàiliệu đích. Đặc tính type cho biết kiểu dữ liệu. Ví dụ dưới
đây là tên sản phẩm:
<xsd:element name="productName" type="xsd:string" />
Và ví dụ dưới đây là giá sản phẩm:
<xsd:element name="productPrice" type="xsd:decimal" />
Bạn có thể tìm hiểu các kiểu dữ liệu Schema tại [http://www.w3.org/TR/xmlschema-2].
Chúng ánh xạ đến các kiểu dữ liệu .NET và bao gồm string, int, long, decimal, float,
dateTime, boolean, base64Binary
Cả productName và productPrice đều là các kiểu đơn giản vì chúng chỉ chứa dữ liệ
u dạng
ký tự. Các phần tử có chứa các phần tử lồng bên trong được gọi là các kiểu phức tạp. Bạn
có thể lồng chúng vào nhau bằng thẻ <sequence> (nếu thứ tự là quan trọng) hay thẻ <all>
(nếu thứ tự là không quan trọng). Dưới đây là cách lắp phần tử <product> vào danh mục
sản phẩm. Chú ý rằng, các đặc tính luôn được khai báo sau các phần tử, và chúng không
được nhóm với thẻ <sequence> hay <all> vì thứ tự không quan trọng.
<xsd:complexType name="product">
<xsd:sequence>
<xsd:element name="productName" type="xsd:string"/>
<xsd:element name="productPrice" type="xsd:decimal"/>
<xsd:element name="inStock" type="xsd:boolean"/>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:integer"/>
</xsd:complexType>
Theo mặc định, một phần tử có thể xuất hiện đúng một lần trong một tài liệu. Nhưng bạn
có thể cấu hình điều này bằng cách chỉ định các đặc tính maxOccurs và minOccurs. Ví dụ
dưới đây không giới hạn số lượng sản phẩm trong danh mục:
<xsd:element name="product" type="product" maxOccurs="unbounded" />
Dưới đây là Schema cho danh mục sản phẩm:
<?xml version="1.0"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<! Định nghĩa product (kiểu phức). >
<xsd:complexType name="product">
<xsd:sequence>
<xsd:element name="productName" type="xsd:string"/>
<xsd:element name="productPrice" type="xsd:decimal"/>
<xsd:element name="inStock" type="xsd:boolean"/>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:integer"/>
</xsd:complexType>
<! Đây là cấu trúc mà tàiliệu phải tuân theo.
Bắt đầu với phần tử productCatalog. >
<xsd:element name="productCatalog">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="catalogName" type="xsd:string"/>
<xsd:element name="expiryDate" type="xsd:date"/>
<xsd:element name="products">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="product" type="product"
maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
Lớp XmlValidatingReader thực thi tất cả các quy tắc Schema này—bảo đảm tàiliệu là
hợp lệ—và nó cũng kiểm tra tàiliệuXML đã được chỉnh dạng hay chưa (nghĩa là không
có các ký tự bất hợp lệ, tất cả các thẻ mở đều có một thẻ đóng tương ứng, v.v ). Để kiểm
tra một tài liệu, hãy dùng phương thức XmlValidatingReader.Read để duyệt qua từng nút
một. Nếu tìm thấy lỗi, XmlValidatingReader d
ựng lên sự kiện ValidationEventHandler
với các thông tin về lỗi. Nếu muốn, bạn có thể thụ lý sự kiện này và tiếp tục kiểm tra tài
liệu để tìm thêm lỗi. Nếu bạn không thụ lý sự kiện này, ngoại lệ XmlException sẽ được
dựng lên khi bắt gặp lỗi đầu tiên và quá trình kiểm tra sẽ bị bỏ dở. Để kiểm tra một tài
liệu đã được chỉnh dạng hay chưa, bạn có thể
sử dụng XmlValidatingReader mà không
cần đến Schema.
Ví dụ kế tiếp trình bày một lớp tiện ích dùng để hiển thị tất cả các lỗi trong một tàiliệu
XML khi phương thức ValidateXml được gọi. Các lỗi sẽ được hiển thị trong một cửa sổ
Console, và một biến luận lý được trả về để cho biết quá trình kiểm tra thành công hay
thất bại.
using System;
using System.Xml;
using System.Xml.Schema;
public class ConsoleValidator {
// Thiết lập thành true nếu tồn tại ít nhất một lỗi.
private bool failed;
public bool Failed {
get {return failed;}
}
public bool ValidateXml(string xmlFilename, string schemaFilename) {
// Tạo validator.
XmlTextReader r = new XmlTextReader(xmlFilename);
XmlValidatingReader validator = new XmlValidatingReader(r);
validator.ValidationType = ValidationType.Schema;
// Nạp Schema vào validator.
XmlSchemaCollection schemas = new XmlSchemaCollection();
schemas.Add(null, schemaFilename);
validator.Schemas.Add(schemas);
// Thiết lập phương thức thụ lý sự kiện validation.
validator.ValidationEventHandler +=
new ValidationEventHandler(ValidationEventHandler);
failed = false;
try {
// Đọc tất cả dữ liệu XML.
while (validator.Read())
{}
}catch (XmlException err) {
// Điều này xảy ra khi tài liệuXML có chứa ký tự bất
// hợp lệ hoặc các thẻ lồng nhau hay đóng không đúng.
Console.WriteLine("A critical XML error has occurred.");
Console.WriteLine(err.Message);
failed = true;
}finally {
validator.Close();
}
return !failed;
}
private void ValidationEventHandler(object sender,
ValidationEventArgs args) {
failed = true;
// Hiển thị lỗi validation.
Console.WriteLine("Validation error: " + args.Message);
Console.WriteLine();
}
}
.
kém hơn lớp XmlDocument.
Ứng dụng dưới đây ghi và đọc một tài liệu XML bằng lớp XmlTextWriter và
XmlTextReader. Tài liệu này giống với tài liệu đã được. bao gồm:
• WriteStartDocument—ghi phần khởi đầu của tài liệu; và WriteEndDocument, đóng
bất kỳ phần tử nào đang mở ở cuối tài liệu.
• WriteStartElement—ghi