在C#开发过程中,经常需要将对象序列化为XML文件,或者从已有的XML文件生成XSD(XML Schema Definition)文件,以及验证XML文件是否符合XSD定义的规则。本文将详细介绍这三个过程的实现方法。
假设有以下C#对象,希望将它们序列化为XML。
public class OrderList {
    public List<Order> Orders { get; set; }
}
public class Order {
    public OrderSummary OrderSummary { get; set; }
    public Customer Customer { get; set; }
}
public class Address {
    public string AddressLine1 { get; set; }
    public string AddressLine2 { get; set; }
    public string AddressLine3 { get; set; }
    public string City { get; set; }
    public string County { get; set; }
    public string PostCode { get; set; }
}
public class Customer {
    public string Title { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public string Phone { get; set; }
}
public class OrderSummary {
    public List<OrderLine> OrderLines { get; set; }
    public Address DeliveryAddress { get; set; }
    public DateTime DeliveryDate { get; set; }
}
public class OrderLine {
    public decimal ItemQuanity { get; set; }
    public string ItemName { get; set; }
}
    
要将这些对象保存为XML文件,可以使用以下代码:
public static void CreateXmlFile(string filename) {
    Address add = new Address {
        AddressLine1 = "AddressLine1",
        AddressLine2 = "AddressLine2",
        AddressLine3 = "AddressLine3",
        City = "City",
        County = "County",
        PostCode = "PostCode"
    };
    Customer cust = new Customer {
        Email = "Email",
        FirstName = "John",
        LastName = "Barnes",
        Phone = "13311",
        Title = "Mr"
    };
    OrderList orders = new OrderList();
    var orderSummary = new OrderSummary {
        DeliveryAddress = add,
        DeliveryDate = DateTime.Now,
        OrderLines = new List<OrderLine> {
            new OrderLine { ItemQuanity = 150, ItemName = "TestItem1" },
            new OrderLine { ItemQuanity = 250, ItemName = "TestItem2" },
            new OrderLine { ItemQuanity = 4, ItemName = "TestItem3" },
        },
    };
    Order order1 = new Order();
    order1.Customer = cust;
    order1.OrderSummary = orderSummary;
    orders.Orders.Add(order1);
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(OrderList));
    using (FileStream stream = File.OpenWrite(filename)) {
        xmlSerializer.Serialize(stream, orders);
    }
}
    
有多种方法可以从XML文件生成XSD文件,这里选择使用C#编程方式生成XSD文件。
public static void CreateSchemaFromXml(string fileName) {
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(OrderList));
    XmlSchemas schemas = new XmlSchemas();
    XmlSchemaExporter exporter = new XmlSchemaExporter(schemas);
    XmlTypeMapping mapping = new XmlReflectionImporter().ImportTypeMapping(typeof(OrderList));
    exporter.ExportTypeMapping(mapping);
    var schemasData = TrimSchema(schemas);
    using (FileStream stream = File.OpenWrite(fileName)) {
        schemasData.First().Write(stream);
    }
}
private static List<XmlSchema> TrimSchema(XmlSchemas schemas) {
    List<XmlSchema> schemasData = new List<XmlSchema>(
        schemas.Where(s => s.TargetNamespace != "http://www.w3.org/2001/XMLSchema" &&
                           s.TargetNamespace != "http://microsoft.com/wsdl/types/")
    );
    return schemasData;
}
    
以上代码将生成一个有效的XSD文件,可以对其进行修改以添加更多的限制。
使用以下测试代码可以轻松实现XML文件的验证。
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using System.Xml.Schema;
using NUnit.Framework;
namespace XmlTests {
    [TestFixture]
    public class StaticXmlFileTests {
        [TestCase(@"\Xml\BadAgainstSchema\OrdersBADExampleFileNoOrderSummary.xml", false)]
        [TestCase(@"\Xml\BadAgainstSchema\OrdersBADExampleFile_AddressLineTooLong.xml", false)]
        [TestCase(@"\Xml\Good\OrdersGOODExampleFile_FullFeatureSet.xml", true)]
        [TestCase(@"\Xml\Good\OrdersGOODExampleFile_MultipleOrderLines.xml", true)]
        [TestCase(@"\Xml\Good\OrdersGOODExampleFile_MultipleOrders.xml", true)]
        [TestCase(@"\Xml\Good\OrdersGOODExampleFile_SingleOrder.xml", true)]
        [TestCase(@"\Xml\Good\OrdersGOODExampleFile_SingleOrderLine.xml", true)]
        public void TestFileProducesExpectedSchemaValidationResult(string filename, bool exepectedValidationResult) {
            var xmlFile = ObtainFullFilePath(filename);
            var xsdFile = ObtainFullFilePath(@"\Xml\OrdersExampleFile.xsd");
            var xdoc = XDocument.Load(xmlFile);
            var schemas = new XmlSchemaSet();
            using (FileStream stream = File.OpenRead(xsdFile)) {
                schemas.Add(XmlSchema.Read(stream, (s, e) => {
                    var x = e.Message;
                }));
            }
            bool isvalid = true;
            StringBuilder sb = new StringBuilder();
            try {
                xdoc.Validate(schemas, (s, e) => {
                    isvalid = false;
                    sb.AppendLine(string.Format("Line : {0}, Message : {1}", e.Exception.LineNumber, e.Exception.Message));
                });
            } catch (XmlSchemaValidationException) {
                isvalid = false;
            }
            var validationErrors = sb.ToString();
            Assert.AreEqual(exepectedValidationResult, isvalid);
            if (exepectedValidationResult) {
                Assert.AreEqual(string.Empty, validationErrors);
            } else {
                Assert.AreNotEqual(string.Empty, validationErrors);
            }
        }
        private string ObtainFullFilePath(string fileName) {
            var path = TestContext.CurrentContext.TestDirectory;
            return string.Format("{0}{1}", path, fileName);
        }
    }
}