4.6 使用限制派生新类型
使用限制派生新类型是最常见的派生方式,这种派生方式以某个现有类型为基础,通过使用<restriction…/>元素来添加一个或多个约束,从而派生出新类型。
提示
Schema把各种约束称为facet,facet直译为“面”,因此有的书籍也把约束称为“面”。不过笔者认为facet用于对原有数据类型增加额外的限制规则,因此本书将facet译为“约束”。
XML Schema规范共推荐了12种约束,用于对原有数据类型添加额外的限制,这些约束大致如下:
enumeration:指定元素或属性的值是枚举值,只能是一系列值其中之一。
fractionDigits:对于任意精度的十进制数起作用,用于定义小数点后的最大位数。
length:定义元素或属性的值的字符长度。
minExclusive:定义元素或属性的下界值,元素或属性的值必须大于该值。
maxExclusive:定义元素或属性的上界值,元素或属性的值必须小于该值。
minLength:字符串的最小长度。
maxLength:字符串的最大长度。
minInclusive:与minExclusive相似,区别是元素或属性的值可以等于该值。
maxInclusive:与maxExclusive相似,区别是元素或属性的值可以等于该值。
pattern:数据类型的值必须匹配的指定的正则表达式,pattern值必须是一个正则表达式。
totalDigits:指定decimal及其派生类型的数值最大能有几位数(包括小数和整数部分)。
whiteSpace:对字符串中的空白的处理方式,其值可以为:preserve,保留字符串中的空白;replace,将所有的制表符、换行符和回车符都用空格代替;collapse,先执行replace,然后去除结尾处和开头处的空格,并将中间多个相邻的空格压缩成一个空格。
这些约束大致上可分为如表4.4所示6类。
表4.4 Schema约束分类
4.6.1指定基类型的两种方式
通过限制派生新类型就是对原有的数据类型添加一个或多个额外的约束(一共有12种约束),这些约束都必须放在<restriction…/>元素下,由该元素组织并应用到指定的数据类型之上。使用<restriction…/>元素时可以指定两个常用属性:
id:指定该<restriction…/>元素的唯一标识。
base:表明该<restriction…/>元素以哪种已有的类型作为基类型,即其组织的全部约束将对哪种已有类型增加额外的约束。
为了定义一种新的数据类型,Schema需要使用<simpleType…/>或<complexType…/>元素,其中<simpleType…/>元素用于定义新的简单类型,而<complexType…/>元素则用于定义一种新的复杂类型。使用这两个元素时可以指定如下常用属性:
id:指定<simpleType…/>或<complexType…/>元素的唯一标识。
name:为新的数据类型指定数据类型名。
从上面的介绍可以看出:由于<restriction…/>元素通常用于定义新的简单类型,因此它必须作为<simpleType…/>元素的子元素来使用。
如下Schema文档定义了一种新的数据类型age_Type,这种类型以int为基础,增加了最大值限制和最小值限制:
程序清单:codes\04\4.6\assignBase.xsd
接下来在XML文档中使用该<age…/>元素时,它的值必须是0~100之间的int数值:
程序清单:codes\04\4.6\assignBase.xml
使用<restriction…/>元素时也可不指定base属性,而在其内添加<simpleType…/>子元素,其中的<simpleType…/>子元素定义的数据类型将作为基类型。如以下Schema代码所示:
程序清单:codes\04\4.6\subType.xsd
上面的Schema文档中定义了一种price_Type类型,使用<restriction…/>元素定义这种数据类型时没有指定base属性,而是直接使用了一个<simpleType…/>子元素,即以该子元素定义的匿名类型作为基类型。因此上面的price_Type类型就以decimal为基类型,最大值只能到100,最小值只能到0,最多可接受5位数字,小数点后最多2位数字。如下XML文档是有效的:
程序清单:codes\04\4.6\subType.xml
实际上第2种方式并不常用,因为如上所示为<restriction…/>元素增加匿名类型作为基类型,其实完全没有必要分开两次来增加约束,一次就可以了。也就是说,上面的price_Type类型可改为如下定义:
4.6.2 指定类型的两种方式
Schema提供了一致的方式来定义元素和属性,区别只是定义元素使用<element…/>元素,而定义属性使用<attribute…/>元素而已。不管是<element…/>还是<attribute…/>元素,都需要指定一个name属性,用于定义元素名或属性名。除此之外,还必须为元素或属性指定数据类型。为元素或属性指定数据类型有两种方式:
使用type属性指定元素或属性的数据类型。
使用<simpleType…/>或<complexType…/>子元素指定元素或属性的数据类型。
4.6.2.1 指定type属性
<element…/>和<attribute…/>元素都可指定一个type属性,其属性值可以是任意一个合法的数据类型,此处的数据类型既可以是Schema内置的数据类型,也可以是用户自定义的数据类型。
如下Schema文档中定义了2个元素和2个属性:
程序清单:codes\04\4.6\type.xsd
上面的Schema文档非常简单,只是定义了2个元素和2个属性。值得指出的是,由于XML文档中属性不能独立存在(必须依附于某个元素),而上面的Schema并未指明哪两个属性属于哪个元素,因此上面两个属性其实无法使用。
上面的Schema中定义了字符串类型的<books…/>元素,这意味着该元素的内容只能是字符串;定义了日期类型(date)的<开始日期…/>元素,这意味着该元素的内容只能是日期。但这两个元素之间没有定义任何的父子关系,而且它们都是作为<schema…/>元素的子元素直接定义的,因此都可以作为XML文档的根元素使用。
下面的XML文档使用<books…/>元素作为根元素,该文档是有效的:
程序清单:codes\04\4.6\books.xml
下面的XML文档使用<开始日期…/>元素作为根元素,该文档也是有效的:
程序清单:codes\04\4.6\开始日期.xml
提示
在XML Schema的根元素下定义的任何元素都可以作为XML文档的根元素使用。
4.6.2.2 使用子元素
除了使用type属性为元素或属性指定类型之外,Schema还允许使用<simpleType…/>子元素为元素或属性指定数据类型,或使用<complexType…/>子元素为元素指定数据类型。如下Schema直接使用<simpleType…/>子元素为元素指定数据类型:
程序清单:codes\04\4.6\subElement.xsd
对于上述Schema,如下XML文档是有效的:
程序清单:codes\04\4.6\subElement.xml
上面的<books…/>元素的内容是“疯狂Java讲义”,将上面的<books…/>元素的值指定为“轻量级Java EE企业应用实战”也是有效的。
这两种指定元素类型的方式,其实正体现了在Schema中定义数据类型的两种方式:
全局的有名字的数据类型:<simpleType…/>和<complexType…/>元素直接作为<schema…/>元素的子元素使用,需要为<simpleType…/>和<complexType…/>元素指定name属性,其值就是新定义数据类型的名称。
局部的匿名数据类型:<simpleType…/>和<complexType…/>元素作为<element…/>、<attribute…/>或者<restriction…/>元素的子元素使用,用于指定元素或属性的类型,或为<restriction…/>元素指定基类型。
很明显,全局数据类型可以多次重复使用,具有更好的可重用性。其既可以在<element…/>、<attribute…/>元素中通过type属性使用,也可以在<restriction…/>元素中通过base属性使用。
4.6.3 范围约束
这类约束有4种:minInclusive、maxInclusive、minExclusive和maxExclusive,用于对基类型添加最小值、最大值约束。只要基类型是可比较大小的数据类型(如数值、日期、时间类型以及它们的派生类型),就可使用这4种约束类型。
值得指出的是,minInclusive、minExclusive约束指定的最小值必须小于maxInclusive、maxExclusive约束所指定的最大值。
关于这4种约束的使用,前面的Schema文档中已有相应的示例,故此处不再赘述。
4.6.4 长度约束
这类约束有3种:length、minLength和maxLength,主要用于约束基于string的数据类型、QName、anyURI和二进制数据的字符串的长度。除此之外,还可用于约束列表类型列表项的数目。由此可见,这类约束的值必须是正整数。例如如下Schema文档:
程序清单:codes\04\4.6\length_constraint.xsd
上面的Schema文档中定义了一个<username…/>元素,该元素的数据类型以token为基础,并使用length约束其长度只能是5。对于上面的Schema文档,如下XML文档是有效的:
程序清单:codes\04\4.6\length_constraint.xml
由于token类型会去掉字符串前后的空格,并将其中的多个连续空格压缩成一个空格,因此上面的XML文档中<username…/>元素的内容恰好是5个字符。
4.6.5 精度约束
这类约束有2种:totalDigits和fractionDigits,其中totalDigits的值必须是正整数,用于约束decimal及其派生类型,指定该数值最多可包含多少位(不包含小数点);fractionDigits的值必须是非负整数,用于约束decimal及其派生类型,指定该数值小数都分最多可包含多少位。
注意
使用fractionDigits约束decimal派生类型(非decimal类型)时,约束值只能是0。
如果需要定义某件物品的定价最多不能超过500,且小数部分最多包含2位,可在Schema文档中定义如下元素和类型:
程序清单:codes\04\4.6\precision_constraint.xsd
该Schema要求<price…/>元素的值最多只能包含5位(不含小数点),小数部分最多只能有2位。如下XML文档是有效的:
程序清单:codes\04\4.6\precision_constraint.xml
值得指出的是,如果在一个<restriction…>元素中同时使用totalDigits和fractionDigits两个约束,则totalDigits约束的值必须大于或等于fractionDigits约束的值。
4.6.6 枚举约束
枚举约束的作用有点类似于DTD中定义枚举类型的属性,它约束目标值必须是一系列值中的任意一个。不同的是,Schema中的枚举约束用于限制一个已有的类型,这样可派生出一个新的类型,这个新的类型既可用于定于属性,也可用于定义元素。
如下Schema中定义了一个<地址…/>元素,该元素的值只能是“广州”和“上海”其中之一。
程序清单:codes\04\4.6\enumeration.xsd
对于如上Schema,如下XML文档是有效的:
程序清单:codes\04\4.6\enumeration.xml
4.6.7 正则表达式约束
正则表达式约束的功能非常强大,这种强大主要来自于正则表达式,关于正则表达式请参考疯狂Java体系的《疯狂Java讲义》9.5节的介绍。
<pattern…/>元素几乎可用于约束各种数据类型,指定该数据类型的值必须匹配指定的正则表达式。如下Schema文档中定义了一个<price…/>元素,要求其值必须是1XX.X:
程序清单:codes\04\4.6\pattern_constraint.xsd
对于如上Schema文档,如下XML文档是有效的:
程序清单:codes\04\4.6\pattern_constraint.xml
4.6.8 空白处理
whiteSpace约束与前面介绍的各种约束有所不同,它并不是对现有数据类型增加额外的约束,而只是通知Schema如何处理XML文档中的空白,因此它通常不会单独使用,而要与其他约束结合使用。
whiteSpace约束可以接受如下几个值:
preserve:保留值里的所有空白。
replace:将值里出现的所有空白(包括回车、换行和制表符)都替换成空格。
collapse:先执行replace,然后再去除前后的空格,并将中间的多个连续空格压缩成一个空格。
whiteSpace约束可用于所有数据类型,但对除string和normalizedString两种数据类型之外的其他数据类型增加该约束时,约束值只能是collapse。
例如如下Schema文档中定义<简要介绍…/>元素的类型时指定了whiteSpace为collapse,这意味着该元素的实际类型是token——先将所有空白替换成空格,再去除前后的空格,最后将字符串中多个连续的空格压缩成单个空格。
程序清单:codes\04\4.6\whiteSpace_constraint.xsd
上面的Schema文档中定义了一个price_Type类型,该类型以decimal为基础,增加了whiteSpace约束,但此处的约束值只能是collapse。除此之外,还定义了一个<简要介绍…/>元素,该元素的类型以string类型为基础,增加了whiteSpace为collapse的约束,这意味着该数据类型实质上是token类型,并且该类型的字符串长度必须是5。如下XML文档是有效的:
程序清单:codes\04\4.6\whiteSpace_constraint.xsd
上面的<简要介绍…/>元素包含的字符串里前后有多个空格,中间也有多个空格,但Schema会将其前后的空格去除,并将中间的多个空格压缩成一个空格,因此上面的字符串内容的长度就变成了5。
注意
string类型增加whiteSpace为collapse的约束后,实质上就变成了token类型,如果增加的是whiteSpace为replace的约束,则实质上就变成了normalizedString类型。
各种内置类型所能支持的约束如表4.5所示。
表4.5 各种内置类型所能支持的约束
备注:表4.5引自XML Schema Part 0: Primer Second Edition的附录B:Simple Types & their Facets。读者可以通过http://www.w3.org/ TR/xmlschema-0/查看这份文档。