将XSD架构文件嵌入资源使用
上一篇,我讲解到如何使用XSD架构对XML数据文档进行验证。这一篇深入一点来看:那个XSD架构能不能直接嵌入到程序,作为资源存在呢?这样一来,就可以更好的保护这个文件
准备工作:为了测试,请创建一个ConsoleApplication
1. 首先,我们的范例数据比较简单。请保存为Order.xml
<?xml version="1.0" encoding="utf-8" ?> <Order> <OrderID>10248</OrderID> <OrderDate>2009-01-01</OrderDate> <Items> <OrderItem> <ItemNumber>1</ItemNumber> <ProductID>2</ProductID> <Quantity>12.00</Quantity> <UnitPrice>25.6</UnitPrice> </OrderItem> <OrderItem> <ItemNumber>1</ItemNumber> <ProductID>2</ProductID> <Quantity>12.00</Quantity> <UnitPrice>25.6</UnitPrice> </OrderItem> <OrderItem> <ItemNumber>1</ItemNumber> <ProductID>2</ProductID> <Quantity>12.00</Quantity> <UnitPrice>25.6</UnitPrice> </OrderItem> </Items> </Order>
2. 我们直接通过Visual Studio的菜单:“XML”=》“创建架构”来生成一个XSD文件,默认的名字就是Order.xsd
<?xml version="1.0" encoding="utf-8"?> <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="Order"> <xs:complexType> <xs:sequence> <xs:element name="OrderID" type="xs:unsignedShort" /> <xs:element name="OrderDate" type="xs:date" /> <xs:element name="Items"> <xs:complexType> <xs:sequence> <xs:element maxOccurs="unbounded" name="OrderItem"> <xs:complexType> <xs:sequence> <xs:element name="ItemNumber" type="xs:unsignedByte" /> <xs:element name="ProductID" type="xs:unsignedByte" /> <xs:element name="Quantity" type="xs:decimal" /> <xs:element name="UnitPrice" type="xs:decimal" /> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>
我们对其做一点修改
<?xml version="1.0" encoding="utf-8"?> <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="Order"> <xs:complexType> <xs:sequence> <xs:element name="OrderID" type="xs:unsignedShort" /> <xs:element name="OrderDate" type="xs:date" /> <xs:element name="Items"> <xs:complexType> <xs:sequence> <xs:element maxOccurs="unbounded" name="OrderItem" minOccurs="1"> <xs:complexType> <xs:sequence> <xs:element name="ItemNumber" type="xs:unsignedByte" /> <xs:element name="ProductID" type="xs:unsignedByte" /> <xs:element name="Quantity" type="xs:decimal" /> <xs:element name="UnitPrice" type="xs:decimal" /> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>
保存该文件到项目目录,因为它默认是在临时目录创建的
3.将该文件的生成操作设置为“嵌入的资源”
4. 生成该项目,我们查看一下该文件在程序集里面是怎么一个表现形式
我们通过Reflector打开这个exe
请记住这个名字:ConsoleApplication1.Order.xsd
如果你实在不想要前面这一段前缀:ConsoleApplication1,则可以手工修改一下ConsoleApplication1.csproj文件,把里面的RootNameSpace清空
然后,再来看就很简洁了:Order.xsd
5. 编写代码,使用该嵌入资源文件作为架构,进行验证
using System; using System.Collections.Generic; using System.Text; using System.Reflection; using System.Xml; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { string dataFile = "Order.xml"; string namespaceUrl = string.Empty; XmlReaderSettings settings = new XmlReaderSettings(); settings.ValidationType = ValidationType.Schema; settings.Schemas.Add(namespaceUrl, XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream("Order.xsd"))); settings.ValidationEventHandler += new System.Xml.Schema.ValidationEventHandler(settings_ValidationEventHandler); string errorMessage = "这不是一个合乎规范的数据文件"; sb = new StringBuilder(); XmlReader reader = XmlReader.Create(dataFile, settings); try { reader.MoveToContent(); while (reader.Read()) { if (reader.NodeType == XmlNodeType.Document && reader.NamespaceURI != namespaceUrl) { Console.WriteLine("文档不是合法的"); break; } } } catch (XmlException ex) { sb.AppendFormat("{0}\n", ex.Message); } finally { reader.Close(); } if (sb.Length == 0) Console.WriteLine("该文档是合法的"); else Console.WriteLine(sb.ToString()); Console.Read(); } static StringBuilder sb = null; static void settings_ValidationEventHandler(object sender, System.Xml.Schema.ValidationEventArgs e) { sb.AppendFormat("{0}\n", e.Message); } } }
6. 按下F5键进行调试
同样道理,如果故意将XML文件修改一下,则可能导致验证无法通过
最后,仍然要提一下,如果验证的时候,需要考虑命名空间的问题,则上述代码会有问题。因为之前我们介绍过,那种情况下需要两个或者更多个架构文件,而且这些文件之间还需要有所联系。解决方案就是:那些架构文件可以嵌入,但验证之前,仍然需要在磁盘的同一个目录下临时创建起来,然后用磁盘文件的方式去验证。就是不能使用内存流的方式来做,因为它不知道怎么关联。
由此可见,命名空间这个东西实在是麻烦,不是吗?