验证XML文档是否符合议定的XML结构有两种方法,分别是DTD模式与XML Schema。本文主要介绍XML Schema。
一、XML Schema的优点
- XML Schema基于XML,没有专门的语法。
- XML Schema可以像其他XML文件一样解析和处理。
- XML Schema支持一系列的数据类型(int、float、Boolean、date等)。
- XML Schema提供可扩充的数据模型。
- XML Schema支持综合命名空间。
- XML Schema支持属性组。
二、XSD
XSD文档至少要包含:schema根元素和XML模式命名空间的定义、元素定义。需要注意的是XSD中必须定义一个且只能定义一个schema根元素,根元素中包括模式的约束,XML模式命名空间的定义,其他命名空间的定义、版本信息、语言信息和其他一些信息。
1、schema根元素
语法如下:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> ... xsd:schema>
2、元素
语法如下:
<xsd:element name="user" type="xsd:string" />
XSD中元素是利用element标识符来声明,在上面的示例中name属性是元素的名字,type属性是元素值的类型,可以使XML Schema中内置的数据类型或其他类型。
全部元素如下:
元素 | 说明 |
name | 元素的名称 |
type | 元素值的类型 |
minOccurs | 该元素在父元素中最少出现的次数(默认为1,必须大于等于0) |
maxOccurs | 该元素在父元素中最多出现的次数(默认为1,必须大于等于0),当设置为unbounded表示不限制。 |
3、引用元素
引用元素是利用element标记符的ref属性实现的。主要适用于避免在文档中多次定义同一个元素。表示当前元素与被引用的元素相同。
语法如下:
<xsd:element name="user" type="xsd:string" /> <xsd:sequence> <xsd:element ref="user" /> xsd:sequence>
4、别名
别名主要利用element标识符中的属性substitutionGroup实现的。
语法:
<xsd:element name="user" type="xsd:string" substitutionGroup="yonghu" />
该语句表示该行的元素名可以是user或用户,如:
<yonghu>adminyonghu> <user>adminuser>
这两行xml都是符合条件的。
5、设置默认值与固定值
语法如下:
<xsd:element name="city" type="xsd:string" default="xian" /> <xsd:element name="country" type="xsd:string" fixed="china" />
通过default属性的设置,可以在XML文档中没有对city定义时赋予默认值,而是用fixed属性,可以给元素country设定一个固定的值china,并且不允许改变。
6、利用组合器控制结构
1、sequence组合器,定义了一列元素必须按照模式中指定的顺序显示(如果是可选的,也可以不显示)。
<xsd:sequence> <xsd:element name="first" type="xsd:string" /> <xsd:element name="middle" type="xsd:string" /> <xsd:element name="last" type="xsd:string" /> xsd:sequence>
2、all组合器,允许所定义的元素可以按照任意顺序显示,all元素的子元素在默认情况下士必须的,而且每次最多显示一次。
<xsd:all minOccurs="0"> <xsd:element name="first" type="xsd:string" /> <xsd:element name="middle" type="xsd:string" /> <xsd:element name="last" type="xsd:string" /> xsd:all>
(3)choice组合器,允许指定多组声明中的一个,用于互斥情况。
<xsd:choice> <xsd:element name="first" type="xsd:string" /> <xsd:element name="middle" type="xsd:string" /> <xsd:element name="last" type="xsd:string" /> xsd:choice>
7、定义属性
在XML Schema文档中可以按照定义元素的方法定义属性,但受限制程度较高。可以应用在attribute元素定义中的属性如下表所示。
属性 | 含义 |
defalt | 初始默认值 |
fixed | 不能修改和覆盖的固定属性值 |
name | 属性的名称 |
ref | 对前一个属性定义的引用 |
type | 该属性的XSD类型或者简单类型 |
use | 如何使用属性 optional(可选属性,即属性不是必须的,默认是这个)、prohibited(禁止使用)或者required(强制必须)。 |
form | 确定attributeFormDefault的本地址 |
id | 模式文档中属性唯一的ID |
8、创建属性
语法如下:
<xsd:attribute name="age" type="xsd:integer" />
该语句定义了一个名为age的属性,它的值必须为整数。把它添加到模式中时,它必须是schema元素,complexType元素或者attributeGroup元素的子元素。
代码示例:
<xsd:element name="name"> <xsd:complexType> <xsd:sequence> <xsd:element name="first" type="xsd:string" /> xsd:sequence> <xsd:attribute name="age" type="xsd:integer" use="optional" /> xsd:complexType> xsd:element>
以上文档对应有效的XML文档如下:
xml version="1.0"?> <name age="27"> <first>stringfirst> name>
该示例不但说明了如下约束属性,还展示了组合器的用法。
三、XML Schema数据类型
1、Schema基本数据类型
Schema的基本数据类型如下:
数据类型 | 说明 |
boolean | true/false |
datetime | 格式:CCYY-MM-DDThh:mm:ss |
decimal | 任意精度的十进制数字 |
string | 字符串数据 |
int | 整型 |
nonNegativeInteger | 大于或等于0的整型 |
nonPositiveInteger | 小于或等于0的整型 |
short | 短整型 -32768到32767 |
2、约束
内置的数据类型功能虽然已经有一定的限制功能,但是还是远远不足够的,更进一步的约束还是来看看约束。
约束 | 说明 |
enumeration | 用空格分开的一组指定的数值,它把数据类型约束为指定的值 |
fractionDigit | 指定小数点后的最大位数 |
length | 长度单位 |
minExclusive | 下限值 |
maxExclusive | 上限值 |
minLength | 最小长度单位 |
maxLength | 最大长度单位 |
minInclusive | 最小值,所有的值都应该大于或等于该值 |
maxInclusive | 最大值,所有的值都应该小于或等于该值 |
pattern | 数据类型的值必须匹配的指定模式,必须是一个正则表达式 |
totalDigits | 指定小数最大位数的值 |
whiteSpace | 其值为preserve(值中的空格不能改变)、replace(所有的制表符、换行符、回车符都用空格代替)、collapse(执行replace,删除相邻的、结尾处和开头处的空格)。 |
要使用上面约束表的约束,就要利用元素restriction。这个元素中有两个属性:ID属性是模式文档中restriction元素的位置标识符;base属性设置为一个内置的XSD数据类型或者现有的简单类型定义,它是一种被限制的类型。
示例:将一个整数的取值范围设置为1~100之间。
<xsd:restriction base="xsd:int"> <xsd:minInclusive value="1" /> <xsd:maxInclusive value="100" /> xsd:restriction>
3、简单类型
简单类型是对一个节点的可能值进一步限制的自定义数据类型。创建简单类型需要利用simpleType元素,其定义如下:
<simpleType id="ID" name="NCName" final="(#all|((list|union|restriction)))" />
ID属性应唯一地标明文档内的simpleType元素,name不能使用冒号字符。simpleType不能包含元素,也不能有属性,它基本上是一个值,或者是一个值的集合。
例如:
<xsd:simpleType name="USState"> <xsd:restriction base="xsd:string"> <xsd:enumeration value="AK"> <xsd:enumeration value="AL"> <xsd:enumeration value="AR"> xsd:restriction> xsd:simpleType> <xsd:element name="statement" type="USState" />
以上文档对应有效的xml文档如下:
<statement>AKstatement>
注意取值只能够为AK、AR、AL中的一个。
4、列表类型
list可以用来定义列表类型。
<xsd:simpleType name="listOfIntType"> <xsd:list itemType="Integer"/> xsd:simpleType> <xsd:element name="listOfMyInt" type="listOfType"/>
listIfIntType这个类型被定义为一个Integet的列表,元素listOfMyInt的值可以是几个整数,他们之间用空格分开。
有效的xml文档如下:
<listOfMyInt>1 2 3 123listOfMyInt>
5、联合类型
union可以用来定义一个联合类型。例如:
<xsd:simpleType name="zipUnion"> <xsd:union memberTypes="USState listOfMyIntType"/> xsd:simpleType> <xsd:element name="zips" type="zipUnion"/>
用union来定义一个联合类型,里面的成员类型保罗USState和listOfMyIntType,应用了联合类型的元素的值可以是这些原子类型或列表类型中的一个类型的示例,但是一个元素实例不能同时包含两个类型。
有效的XML文档如下:
<zips>CAzips> <zips>9192 192391 129zips> <zips>AKzips>
无效的XML文档如下:
<zips>AL 2231zips>
同时包含两个是错误的。
6、匿名类型
前面定义元素类型的时候总是先定义一个数据类型,然后再把元素的type设成新定义的数据类型。如果这个新的数据类型只会用一次,我们就可以直接设置在元素定义里面,而不用另外来设置。
例如:
<xsd:element name="quantity"> <xsd:simpleType> <xsd:restriction base="xsd:positiveInteger"> <xsd:maxExclusive value="100"/> xsd:restriction> xsd:simpleType> xsd:element>
元素quantity的类型就是一个从1~99的整数。对于这种没有用type引入,直接定义在element元素里面的类型,我们称之为匿名类型。
7、复杂类型
复杂类型的定义必须使用complexType元素,在这里可以包含属性和元素。在复杂类型的使用中,主要是complexType和simpleType配合使用。
8、内容模型
内容模型可以对在XML文档内使用的元素、属性和类型进行限制,确定用户可以再XML实例的那些等级添加自己的元素和属性。
1、any内容模型
在XML中声明元素时,any是默认的内容模型,该模型可以包含文本、元素和空格。
例如:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:element name="name"> <xsd:complexType> <xsd:sequence> <xsd:element name="first" type="xsd:string" /> <xsd:element name="middle" type="OtherNames" /> <xsd:element name="last" type="xsd:string" /> xsd:sequence> xsd:complexType> xsd:element> <xsd:complexType name="OtherNames"> <xsd:sequence> <xsd:any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> xsd:sequence> xsd:complexType> xsd:schema>
例子中xsd:any元素说明该类型允许添加内容。
namespace属性允许的值为:
- ##any:元素可以来自任何命名空间。
- ##other:元素可以来自除了该元素的父元素所在的目标命名空间之外的命名空间。
- ##local:元素不受命名空间限制。
- ##targetNamespace:元素来自父元素的目标命名空间。
processContents属性说明对这里所创建的元素进行验证时所执行的操作。
processContents属性取值有如下三种:
- strict:表明XML处理器必须获得和哪些命名空间相关联的模式,并验证元素和属性。
- lax:与strict类似,只是如果处理器找不到模式文档,也不会出现错误。
- skip:不利用模式文档验证XML文档。
上述模式的一个有效实例:
xml version="1.0" encoding="utf-8" ?> <name> <first>santldfirst> <middle> <nameInChina>SannameInChina> middle> <last>wanglast> name>
2、空内容模型(empty)
有时候元素根本没有内容,它的内容模型是空。为了定义内容是空的类型,我们可以通过这样的方式:首先定义一个元素,它只能包含子元素而不能包含元素内容,然后又不定义任何子元素,依靠这样的方式,就能够定义出内容模型为空的元素。
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:element name="internationalPrice"> <xsd:complexType> <xsd:complexContent> <xsd:restriction base="xsd:anyType"> <xsd:attribute name="currency" type="xsd:string"/> <xsd:attribute name="value" type="xsd:decimal"/> xsd:restriction> xsd:complexContent> xsd:complexType> xsd:element> xsd:schema>
有效的XML文档如下:
<internationalPrice currency="ERU" value="/423.46"/>
无效的文档示例:
<internationalPrice currency="ERU" value="/423.46"/>这是错误的internationalPrice>
3、混合内容模型(mixed)
它包含文本、内容和属性。在complexType元素上把mixed属性的值设为true,就声明了一个mixed内容模型。例如:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:element name="contact"> <xsd:complexType mixed="true"> <xsd:sequence> <xsd:element name="first" type="xsd:string" /> xsd:sequence> xsd:complexType> xsd:element> xsd:schema>
上述模式的一个有效实例如下:
xml version="1.0" encoding="utf-8" ?> <contact> My first is <first>Santldfirst>. contact>
四、Visual Studio设计器创建XSD Schema
用Visual Studio创建XSD Schema还是较为容易的。因为IDE提供可视化工具用于构建元素、简单类型和复杂类型等。首先添加一个新的Schema文件。
英文版的名字是XML Schema,中文版是XML 架构。很奇怪,按照书上说的,添加一个XML Schema文件之后,工具箱会有很多工具,但是我的Visual Studio 2010里面并没有添加任何工具。此处留到以后再补充。
五、.Net验证XML文档
为了在XML文档中关联外部的 XSD Schema文件,要对XML文档以及XSD Schema文件作出相应的修改,具体的修改如下示例所示:
XML文件:
xml version="1.0" encoding="utf-8" ?> <person xmlns="http://www.xxx.com/xxx"> <name>张飞1111111name> <age>24age> person>
XML Schema文件:
xml version="1.0" encoding="utf-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.xxx.com/xxx"> <xs:element name="person"> <xs:complexType> <xs:sequence> <xs:element name="name"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:maxLength value="4"/> <xs:minLength value="2"/> xs:restriction> xs:simpleType> xs:element> <xs:element name="age"> <xs:simpleType> <xs:restriction base="xs:positiveInteger"> <xs:maxExclusive value="100"/> <xs:minExclusive value="1"/> xs:restriction> xs:simpleType> xs:element> xs:sequence> xs:complexType> xs:element> xs:schema>
代码文件:XmlDocument验证文件
static void Main(string[] args) { XmlDocument doc = new XmlDocument(); //创建文档 doc.Schemas.Add(null, @"C:\Users\Administrator\Desktop\ConsoleApplication1\ConsoleApplication1\person.xsd"); //添加验证架构文件,null为使用默认的命名空间 doc.Load(@"C:\Users\Administrator\Desktop\ConsoleApplication1\ConsoleApplication1\person.xml"); //加载xml文件 doc.Validate(SettingsValidationEventHandler); //执行验证操作,错误处理方法为参数SettingsValidationEventHandler Console.WriteLine("验证通过"); //如果验证通过才会执行到此 Console.ReadKey(); } static void SettingsValidationEventHandler(object sender, ValidationEventArgs e) { if (e.Severity == XmlSeverityType.Warning) { Console.Write("警告信息: "); Console.WriteLine(e.Message); } else if (e.Severity == XmlSeverityType.Error) { Console.Write("错误信息: "); Console.WriteLine(e.Message); } else { } Console.ReadKey(); }
代码文件:XmlReader版本:
static void Main(string[] args) { XmlReaderSettings Settings = new XmlReaderSettings(); // Settings.Schemas.Add(null, @"C:\Users\Administrator\Desktop\ConsoleApplication1\ConsoleApplication1\person.xsd"); Settings.ValidationType = ValidationType.Schema; Settings.ValidationEventHandler += new ValidationEventHandler(SettingsValidationEventHandler); XmlReader reader = XmlReader.Create(@"C:\Users\Administrator\Desktop\ConsoleApplication1\ConsoleApplication1\person.xml", Settings); while (reader.Read()) { } reader.Close(); Console.WriteLine("验证成功!"); Console.ReadKey(); } static void SettingsValidationEventHandler(object sender, ValidationEventArgs e) { if (e.Severity == XmlSeverityType.Warning) { Console.Write("警告信息: "); Console.WriteLine(e.Message); } else if (e.Severity == XmlSeverityType.Error) { Console.Write("错误信息: "); Console.WriteLine(e.Message); } else { } Console.ReadKey(); }