181 Chương 5: XML 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ệu XML 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) { 182 Chương 5: XML failed = true; // Hiển thị lỗi validation. Console.WriteLine("Validation error: " + args.Message); Console.WriteLine(); } } Dưới đây là cách sử dụng lớp này để xác nhận tính hợp lệ của danh mục sản phẩm: using System; public class ValidateXml { private static void Main() { ConsoleValidator consoleValidator = new ConsoleValidator(); Console.WriteLine("Validating ProductCatalog.xml."); bool success = consoleValidator.ValidateXml("ProductCatalog.xml", "ProductCatalog.xsd"); if (!success) { Console.WriteLine("Validation failed."); }else { Console.WriteLine("Validation succeeded."); } Console.ReadLine(); } } Nếu tài liệu hợp lệ thì sẽ không có thông báo nào xuất hiện, và biến success sẽ được thiết lập thành true . Nhưng xét xem điều gì sẽ xảy ra nếu bạn sử dụng một tài liệu phá vỡ các quy tắc Schema, chẳng hạn file ProductCatalog_Invalid.xml như sau: <?xml version="1.0" ?> <productCatalog> <catalogName>Acme Fall 2003 Catalog</catalogName> <expiryDate>Jan 1, 2004</expiryDate> 183 Chương 5: XML <products> <product id="1001"> <productName>Magic Ring</productName> <productPrice>$342.10</productPrice> <inStock>true</inStock> </product> <product id="1002"> <productName>Flying Carpet</productName> <productPrice>982.99</productPrice> <inStock>Yes</inStock> </product> </products> </productCatalog> Nếu bạn kiểm tra tài liệu này, biến success sẽ được thiết lập thành false và kết xuất sẽ cho biết các lỗi: Validating ProductCatalog_Invalid.xml. Validation error: The 'expiryDate' element has an invalid value according to its data type. An error occurred at file:///I:/CSharp/Chuong05/05-08/ bin/Debug/ProductCatalog_Invalid.xml, (4, 30). Validation error: The 'productPrice' element has an invalid value according to its data type. An error occurred at file:///I:/CSharp/Chuong05/05-08/ bin/Debug/ProductCatalog_Invalid.xml, (9, 36). Validation error: The 'inStock' element has an invalid value according to its data type. An error occurred at file:///I:/CSharp/Chuong05/05-08/ bin/Debug/ProductCatalog_Invalid.xml, (15, 27). Validation failed. Cuối cùng, nếu muốn xác nhận tính hợp lệ của một tài liệu XML và rồi xử lý nó, bạn có thể sử dụng XmlValidatingReader để quét tài liệu khi nó được đọc vào một XmlDocument trong-bộ- nhớ: XmlDocument doc = new XmlDocument(); XmlTextReader r = new XmlTextReader("ProductCatalog.xml"); XmlValidatingReader validator = new XmlValidatingReader(r); // Nạp Schema vào validator. validator.ValidationType = ValidationType.Schema; 184 Chương 5: XML XmlSchemaCollection schemas = new XmlSchemaCollection(); schemas.Add(null, "ProductCatalog.xsd"); validator.Schemas.Add(schemas); // Nạp và kiểm tra tài liệu cùng một lúc. try { doc.Load(validator); // (Validation thành công.) }catch (XmlSchemaException err) { // (Validation thất bại.) } 9. 9. S d ng XML Serialization v i các đ i t ng tùy bi nử ụ ớ ố ượ ế S d ng XML Serialization v i các đ i t ng tùy bi nử ụ ớ ố ượ ế Bạn cần sử dụng XML như một định dạng tuần tự hóa ( serialization format ). Tuy nhiên, bạn không muốn xử lý XML trực tiếp trong mã lệnh, mà muốn tương tác với dữ liệu bằng các đối tượng tùy biến. Sử dụng lớp System.Xml.Serialization.XmlSerializer để chuyển dữ liệu từ đối tượng của bạn sang XML , và ngược lại. Bạn cũng có thể đánh dấu mã lệnh của lớp bằng các đặc tính để tùy biến biểu diễn XML của nó. Lớp XmlSerializer cho phép chuyển các đối tượng thành dữ liệu XML, và ngược lại. Lớp này đủ thông minh để tạo đúng các mảng khi nó tìm thấy các phần tử lồng bên trong. Các yêu cầu khi sử dụng XmlSerializer : • XmlSerializer chỉ tuần tự hóa các thuộc tính và các biến công khai. • Các lớp cần tuần tự hóa phải chứa một phương thức khởi dựng mặc định không có đối số. XmlSerializer sẽ sử dụng phương thức khởi dựng này khi tạo đối tượng mới trong quá trình giải tuần tự hóa. • Các thuộc tính của lớp phải là khả-đọc (readable) và khả-ghi (writable). Đó là vì XmlSerializer sử dụng hàm truy xuất thuộc tính get để lấy thông tin và hàm truy xuất thuộc tính set để phục hồi dữ liệu sau khi giải tuần tự hóa. Bạn cũng có thể lưu trữ các đối tượng theo định dạng dựa-trên- XML bằng cách sử dụng .NET Serialization và System.Runtime.Serialization.Formatters.Soap. SoapFormatter . Trong trường hợp này, bạn chỉ cần làm cho lớp của bạn trở thành khả-tuần-tự-hóa, không cần cung cấp phương thức khởi dựng mặc định hay bảo đảm tất cả các thuộc tính là khả ghi. Tuy nhiên, cách này không cho bạn quyền kiểm soát trên định dạng XML đã-được-tuần-tự-hóa. 185 Chương 5: XML Để sử dụng XML serialization, trước hết bạn phải đánh dấu các đối tượng dữ liệu với các đặc tính cho biết phép ánh xạ sang XML. Các đặc tính này thuộc không gian tên System.Xml.Serialization và bao gồm: • XmlRoot —cho biết tên phần tử gốc của file XML. Theo mặc định, XmlSerializer sẽ sử dụng tên của lớp. Đặc tính này có thể được áp dụng khi khai báo lớp. • XmlElement —cho biết tên phần tử dùng cho một thuộc tính hay biến công khai. Theo mặc định, XmlSerializer sẽ sử dụng tên của thuộc tính hay biến công khai. • XmlAttribute —cho biết một thuộc tính hay biến công khai sẽ được tuần tự hóa thành một đặc tính (không phải phần tử), và chỉ định tên đặc tính. • XmlEnum —cấu hình phần text sẽ được sử dụng khi tuần tự hóa các giá trị liệt kê. Nếu bạn không sử dụng XmlEnum , tên của hằng liệt kê sẽ được sử dụng. • XmlIgnore —cho biết một thuộc tính hay biến công khai sẽ không được tuần tự hóa. Ví dụ, xét danh mục sản phẩm đã được trình bày trong mục 5.1. Bạn có thể mô tả tài liệu XML này bằng các đối tượng ProductCatalog và Product như sau: using System; using System.Xml.Serialization; [XmlRoot("productCatalog")] public class ProductCatalog { [XmlElement("catalogName")] public string CatalogName; // Sử dụng kiểu dữ liệu ngày (bỏ qua phần giờ). [XmlElement(ElementName="expiryDate", DataType="date")] public DateTime ExpiryDate; // Cấu hình tên thẻ. [XmlArray("products")] [XmlArrayItem("product")] public Product[] Products; public ProductCatalog() { // Phương thức khởi dựng mặc định (dùng khi giải tuần tự hóa). } public ProductCatalog(string catalogName, DateTime expiryDate) { this.CatalogName = catalogName; 186 Chương 5: XML this.ExpiryDate = expiryDate; } } public class Product { [XmlElement("productName")] public string ProductName; [XmlElement("productPrice")] public decimal ProductPrice; [XmlElement("inStock")] public bool InStock; [XmlAttributeAttribute(AttributeName="id", DataType="integer")] public string Id; public Product() { // Phương thức khởi dựng mặc định (dùng khi giải tuần tự hóa). } public Product(string productName, decimal productPrice) { this.ProductName = productName; this.ProductPrice = productPrice; } } Chú ý rằng, các lớp này sử dụng các đặc tính XML Serialization để đổi tên phần tử (sử dụng kiểu ký hiệu Pascal 1 trong tên thành viên lớp, và kiểu ký hiệu lưng lạc đà 2 trong tên thẻ XML), cho biết các kiểu dữ liệu không rõ ràng, và chỉ định các phần tử <product> sẽ được lồng bên trong <productCatalog> như thế nào. Bằng cách sử dụng các lớp tùy biến này và đối tượng XmlSerializer , bạn có thể chuyển XML thành các đối tượng và ngược lại. Đoạn mã dưới đây tạo một đối tượng ProductCatalog mới, 1 Pascal casing: Mẫu tự đầu tiên của các chữ đều viết hoa, ví dụ SomeOtherName 2 Camel casing: Mẫu tự đầu tiên của chữ đầu viết thường, mẫu tự đầu tiên của các chữ đi sau viết hoa, ví dụ someOtherName 187 Chương 5: XML tuần tự hóa đối tượng thành tài liệu XML, giải tuần tự hóa tài liệu thành đối tượng, và rồi hiển thị tài liệu này: using System; using System.Xml; using System.Xml.Serialization; using System.IO; public class SerializeXml { private static void Main() { // Tạo danh mục sản phẩm. ProductCatalog catalog = new ProductCatalog("New Catalog", DateTime.Now.AddYears(1)); Product[] products = new Product[2]; products[0] = new Product("Product 1", 42.99m); products[1] = new Product("Product 2", 202.99m); catalog.Products = products; // Tuần tự hóa danh mục ra file. XmlSerializer serializer = new XmlSerializer(typeof(ProductCatalog)); FileStream fs = new FileStream("ProductCatalog.xml", FileMode.Create); serializer.Serialize(fs, catalog); fs.Close(); catalog = null; // Giải tuần tự hóa danh mục từ file. fs = new FileStream("ProductCatalog.xml", FileMode.Open); catalog = (ProductCatalog)serializer.Deserialize(fs); // Tuần tự hóa danh mục ra cửa sổ Console. serializer.Serialize(Console.Out, catalog); Console.ReadLine(); } 188 Chương 5: XML } 10. 10. T o XML Schema cho m t l p .NETạ ộ ớ T o XML Schema cho m t l p .NETạ ộ ớ Bạn cần tạo một XML Schema dựa trên một hay nhiều lớp C# . Điều này cho phép bạn kiểm tra tính hợp lệ của các tài liệu XML trước khi giải tuần tự hóa chúng với XmlSerializer . Sử dụng tiện ích dòng lệnh XML Schema Definition Tool ( xsd.exe —đi kèm với . NET Framework ). Chỉ định tên của assembly làm đối số dòng lệnh, và thêm đối số /t:[TypeName] để cho biết kiểu cần chuyển đổi. Mục 5.9 đã trình bày cách sử dụng XmlSerializer để tuần tự hóa đối tượng .NET thành XML, và giải tuần tự hóa XML thành đối tượng .NET. Nhưng nếu muốn sử dụng XML như một phương cách để tương tác với các ứng dụng khác, quy trình nghiệp vụ, hay các ứng dụng phi- Framework, bạn sẽ cần xác nhận tính hợp lệ của XML trước khi giải tuần tự hóa nó. Bạn cũng sẽ cần tạo một tài liệu XML Schema định nghĩa cấu trúc và các kiểu dữ liệu được sử dụng trong định dạng XML của bạn, để các ứng dụng khác có thể làm việc với nó. Một giải pháp là sử dụng tiện ích dòng lệnh xsd.exe. Tiện ích xsd.exe đi kèm với .NET Framework. Nếu đã cài đặt Microsoft Visual Studio .NET, bạn sẽ tìm thấy nó trong thư mục C:\Program Files\Microsoft Visual Studio .NET\FrameworkSDK\Bin. Tiện ích xsd.exe có thể tạo ra XML Schema từ một assembly đã được biên dịch. Bạn chỉ cần cung cấp tên file và cho biết lớp mô tả tài liệu XML với đối số /t:[TypeName] . Ví dụ, xét các lớp ProductCatalog và Product đã được trình bày trong mục 5.9. Bạn có thể tạo XML Schema cho một danh mục sản phẩm với dòng lệnh sau: xsd 05-09.exe /t:ProductCatalog Bạn chỉ cần chỉ định lớp ProductCatalog trên dòng lệnh, vì lớp này mô tả tài liệu XML. XML Schema được tạo ra trong ví dụ này (có tên mặc định là schema0.xsd) sẽ mô tả đầy đủ một danh mục sản phẩm, với các item sản phẩm lồng bên trong. Bây giờ, bạn có thể sử dụng XmlValidatingReader (đã được trình bày trong mục 5.8) để kiểm tra tính hợp lệ của tài liệu XML dựa vào XML Schema này. 11. 11. T o l p t m t XML Schemaạ ớ ừ ộ T o l p t m t XML Schemaạ ớ ừ ộ Bạn cần tạo một hay nhiều lớp C# dựa trên một XML Schema ; để sau đó, bạn có thể tạo một tài liệu XML theo định dạng phù hợp bằng các đối tượng này và XmlSerializer . Sử dụng tiện ích dòng lệnh xsd.exe (đi kèm với .NET Framework ). Chỉ định tên file Schema làm đối số dòng lệnh, và thêm đối số /c để cho biết bạn muốn tạo mã lệnh cho lớp. 189 Chương 5: XML Mục 5.10 đã giới thiệu tiện ích dòng lệnh xsd.exe, tiện ích này có thể được sử dụng để tạo XML Schema dựa trên định nghĩa lớp. Quá trình ngược lại (tạo mã lệnh C# dựa trên một tài liệu XML Schema) cũng có thể xảy ra. Việc này hữu ích khi bạn muốn ghi một định dạng XML nào đó, nhưng lại không muốn tạo tài liệu này bằng cách ghi từng nút một với lớp XmlDocument hay XmlTextWriter . Thay vào đó, bằng cách sử dụng xsd.exe, bạn có thể tạo ra một tập đầy đủ các đối tượng .NET. Kế đó, bạn có thể tuần tự hóa các đối tượng này thành biểu diễn XML bằng XmlSerializer , như được mô tả trong mục 5.9. Để tạo mã lệnh từ một XML Schema, bạn chỉ cần cung cấp tên file Schema và thêm đối số /c để cho biết bạn muốn tạo ra lớp. Ví dụ, xét XML Schema đã được trình bày trong mục 5.8. Bạn có thể tạo mã lệnh C# từ Schema này với dòng lệnh sau: xsd ProductCatalog.xsd /c Lệnh này sẽ tạo ra một file (ProductCatalog.cs) gồm hai lớp: Product và productCalalog . Hai lớp này tương tự với hai lớp đã được tạo trong mục 5.9. 12. 12. Th c hi n phép bi n đ i XSLự ệ ế ổ Th c hi n phép bi n đ i XSLự ệ ế ổ Bạn cần biến đổi một tài liệu XML thành một tài liệu khác bằng XSLT stylesheet . Sử dụng lớp System.Xml.Xsl.XslTransform . Nạp XSLT stylesheet bằng phương thức XslTransform.Load , và tạo tài liệu kết xuất bằng phương thức Transform (cần cung cấp tài liệu nguồn). XSLT (hay XSL Transforms) là một ngôn ngữ dựa-trên-XML, được thiết kế để biến đổi một tài liệu XML thành một tài liệu khác. XSLT có thể được sử dụng để tạo một tài liệu XML mới với cùng dữ liệu nhưng được sắp xếp theo một cấu trúc khác hoặc để chọn một tập con dữ liệu trong một tài liệu. Nó cũng có thể được sử dụng để tạo một kiểu tài liệu có cấu trúc khác. XSLT thường được sử dụng theo cách này để định dạng một tài liệu XML thành một trang HTML. XSLT là một ngôn ngữ đa năng, và việc tạo XSL Transforms vượt quá phạm vi quyển sách này. Tuy nhiên, bạn có thể học cách tạo các tài liệu XSLT đơn giản bằng cách xem một ví dụ cơ bản. Mục này sẽ biến đổi tài liệu orders.xml (đã được trình bày trong mục 5.6) thành một tài liệu HTML và rồi hiển thị kết quả. Để thực hiện phép biến đổi này, bạn sẽ cần XSLT stylesheet như sau: <?xml version="1.0" encoding="UTF-8" ?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" > <xsl:template match="Order"> <html><body><p> Order <b><xsl:value-of select="Client/@id"/></b> for <xsl:value-of select="Client/Name"/></p> <table border="1"> 190 Chương 5: XML <td>ID</td><td>Name</td><td>Price</td> <xsl:apply-templates select="Items/Item"/> </table></body></html> </xsl:template> <xsl:template match="Items/Item"> <tr> <td><xsl:value-of select="@id"/></td> <td><xsl:value-of select="Name"/></td> <td><xsl:value-of select="Price"/></td> </tr> </xsl:template> </xsl:stylesheet> Về cơ bản, mọi XSL stylesheet gồm một tập các template. Mỗi template so trùng với các phần tử trong tài liệu nguồn và rồi mô tả các phần tử được so trùng để tạo nên tài liệu kết quả. Để so trùng template, tài liệu XSLT sử dụng biểu thức XPath, như được mô tả trong mục 5.6. Stylesheet vừa trình bày ở trên (orders.xslt) gồm hai template (là các con của phần tử stylesheet gốc). Template đầu tiên trùng khớp với phần tử Order gốc. Khi bộ xử lý XSLT tìm thấy một phần tử Order , nó sẽ ghi ra các thẻ cần thiết để bắt đầu một bảng HTML với các tiêu đề cột thích hợp và chèn dữ liệu về khách hàng bằng lệnh value-of (ghi ra kết quả dạng text của một biểu thức XPath). Trong trường hợp này, các biểu thức XPath ( Client/@id và Client/Name ) trùng với đặc tính id và phần tử Name . Kế tiếp, lệnh apply-templates được sử dụng để phân nhánh và xử lý các phần tử Item nằm trong. Điều này là cần thiết vì có thể có nhiều phần tử Item . Mỗi phần tử Item được so trùng bằng biểu thức Items/Item (nút gốc Order không được chỉ định vì Order chính là nút hiện tại). Cuối cùng, các thẻ cần thiết sẽ được ghi ra để kết thúc tài liệu HTML. Nếu thực thi phép biến đổi này trên file orders.xml (đã trình bày trong mục 5.6), bạn sẽ nhận được kết quả (tài liệu HTML) như sau: <html> <body> <p> Order <b>ROS-930252034</b> for Remarkable Office Supplies</p> <table border="1"> <td>ID</td> <td>Name</td> . các mảng khi nó tìm thấy các phần tử lồng bên trong. Các yêu cầu khi sử dụng XmlSerializer : • XmlSerializer chỉ tuần tự hóa các thuộc tính và các biến công khai. • Các lớp cần tuần tự hóa phải. thẻ XML), cho biết các kiểu dữ liệu không rõ ràng, và chỉ định các phần tử <product> sẽ được lồng bên trong <productCatalog> như thế nào. Bằng cách sử dụng các lớp tùy biến này. đổi. Mục 5.9 đã trình bày cách sử dụng XmlSerializer để tuần tự hóa đối tượng .NET thành XML, và giải tuần tự hóa XML thành đối tượng .NET. Nhưng nếu muốn sử dụng XML như một phương cách để tương