XML系列——原来XML Schema并不难

1.前言

大家都知道XML Schema是对XML文件的数据结构的定义规范,它完全可以取代DTD。在没有仔细地去了解XML Schema之前,一直都觉得它挺难的。在看了W3School教程之后,发现也并没有想像的那么难,可能W3School教程并没有很全面地介绍XML Schema,但个人觉得已经很实用了。下面是对XML Schema的一个学习总结。Notic:文章中有些XSD元素或属性没有解释说明的,请到W3School查看。

在学习一样新东西的时候,概念往往是一切知识的基础,有了清晰明了的概念后,在深入探讨的时候往往能事半功倍。OK,那我们就从概念开始吧!

2.Schema中的一些概念

注:带*号的概念是自己总结出来的概念名称,所以这里说的一些概念没看懂没关系,有了印象后带着疑问继续往下看吧。

2.1.XML Schema

XML Schema定义了XML文件的数据结构,与XSD(XML Schema Define)是同一个概念,而事实上,XML Schema文件的后缀名是以xsd结尾的。XSD只是一个标准的XML文件。

2.2.XSD中的数据类型

与平时编程说到的数据类型非常相似,只是在XSD中,有它自己的一套数据类型罢了。比如:string代表字符串类型,date代表了日期类型等等,具体可以查看W3School中关于XSD数据类型的说明。

2.3.元素的结构类型*

在XSD中,每一个元素的定义都必须提供它的结构类型,XSD中一共有两种结构类型:复杂类型和简单类型。它们分别对应了XSD中的<complexType>和<simpleType>。

2.3.1.复杂类型——complexType

所有带属性的元素或含有子元素的元素都属于复杂类型。复杂类型由子元素定义和属性定义组成。

2.3.2.简单类型——simpleType

所有只包含文本节点的元素或所有属性都属于简单类型。对于简单类型,可以理解成是XSD数据类型的一个扩展,它也是一种数据类型。

2.4.元素的内容*

元素的内容指的是复杂类型元素的开始标签与结束标签之间的内容(注意,元素内容不适用于简单类型的元素,也就是只包含文本节点的元素)。XSD中有两种类型的元素内容:复杂内容和简单内容。它们分别对应了XSD中的两个标签<complexContent>和<simpleContent>。这两个标签在XSD中主要是为了扩展或修改复杂类型complexType的限制,除此外,这两个标签没有其他作用。

2.4.1.复杂内容——complexContent

元素的内容不是简单的文本节点都可以看作是复杂内容。一般用于扩展复杂类型的元素

2.4.2.简单内容——simpleContent

元素的内容只有简单的文本节点可以看作是简单内容。个人觉得simpleContent只在一种情况下有用:一个带属性的元素,而且它内部只有文本节点。

3.XML实例场景

为了更好地说明事物,我们现在定义一个这样的一个XML文件:

school.xml
<?xml version="1.0" encoding="utf-8" ?>
<school xmlns="http://www.sin90lzc.com/school" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.sin90lzc.com/school school.xsd">
    <teachers>
        <teacher head="true">
            <name>Tim</name>
            <subject main="true">Math</subject>
        </teacher>
        <teacher>
            <name>Rain</name>
            <subject>英语</subject>
        </teacher>
        <teacher>
            <name>Petter</name>
            <subject>语文</subject>
        </teacher>
    </teachers>
    <students>
        <student>
            <name>Sally</name>
            <teacher ref="Tim" />
        </student>
        <student>
            <name>Edison</name>
            <teacher ref="Petter" />
        </student>
    </students>
</school>

关于school.xml的中文表述:

一个学校有多个老师和多个学生。在老师中,只有一名是校长(head="true"),每个老师的名字不能相同而且首字母必须大写,每个老师都只能教一门课程,课程的重要性通过属性main="true"指示(这样的一个XML结构是有问题的,但在这里我们的目的是为了表达XSD中的一些功能),课程的名称可以用英文表示,也可以用中文表示,而且只包含语文、数学、英语三个课程。在学生中,学生的名字也不能相同而且首字母必须大写,每个学生都有他们的直属指导老师,这里的老师引用必须是<teachers>中定义的老师的其中一名。

下面终于可以动手写一份XSD来定义school.xml的数据结构。

4.元素定义

4.1.XSD中的根元素<schema>

<?xml version="1.0" encoding="utf-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://www.sin90lzc.com/school" xmlns:s="http://www.sin90lzc.com/school"
    xmlns="http://www.sin90lzc.com/school" elementFormDefault="qualified">
</xs:schema>
  • xmlns:xs="http://www.w3.org/2001/XMLSchema":定义了XSD文档的命名空间为xs
  • targetNamespace="http://www.sin90lzc.com/school":目标命名空间,即需要较验的文档的命名空间
  • xmlns:s="http://www.sin90lzc.com/school" xmlns="http://www.sin90lzc.com/school":定义了默认的命令空间与s命令空间是相同的命名空间,这样做的目的是因为文档中会用到XPath,而XPath不能识别默认的命令空间,在XPath中需要使用s命名空间。

  • elementFormDefault="qualified":指示使用该XSD的xml文件必须使用目标命名空间

4.2.元素定义公式

XSD有三种定义元素的方式:

  1. 全定义方式
    <xs:element name="元素名称">
        <元素的结构类型定义></元素的结构类型定义>
    </xs:element>
  2. 结构类型/数据类型引用方式
    <xs:element name="元素名称" type="元素的结构类型名称|数据类型名称" />
  3. 元素引用方式
    <xs:element ref="引用元素定义的ID" />

为了更直觉地展示XSD,首先采用全定义方式来组织XSD文件,在最后会使用第二,第三种方式来使我们的XSD文件更清晰,更易读。

4.3.<school>元素定义

由于<school>元素内包含两个子元素<teachers>和<students>,因此它属于复杂类型,应使用<complexType>来定义结构类型。

<xs:schema ...>
    <xs:element name="school">
        <xs:complexType>
        </xs:complexType>
    </xs:element>
</xs:schema> 

如概念中说到的,复杂类型由子元素定义和属性定义组成。而所有的子元素的定义都必须使用<xs:sequence>或<xs:all>或<xs:choice>包裹,指示子元素的一些特征。

<xs:element name="school">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="teachers"></xs:element>
            <xs:element name="students"></xs:element>
        </xs:sequence>
    </xs:complexType>
</xs:element>

4.4.<teachers>元素定义

现在我们把焦点放在<teachers>元素的定义上,<teachers>元素内包含了多个<teacher>的子元素,因此,它属于复杂类型。

<xs:element name="teachers">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="teacher" minOccurs="1" maxOccurs="unbounded"></xs:element>
        </xs:sequence>
    </xs:complexType>
</xs:element>

minOccurs和maxOccurs属性分别用于限制元素最少或最多可以重复使用的次数,unbounded表示没有限制次数。

4.5.<teacher>元素定义

<teacher>带有属性head和子元素<name> <subject>,因此它也是属于复杂类型。

<xs:element name="teacher" minOccurs="1" maxOccurs="unbounded">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="name"></xs:element>
            <xs:element name="subject"></xs:element>
        </xs:sequence>
        <xs:attribute name="head" type="xs:boolean"></xs:attribute>
    </xs:complexType>
</xs:element> 

4.6.<name>元素定义

<name>元素只包含有文本节点,因此它属于简单类型simpleType。而且对name元素中的文本要求是首字母大写,这个要求可以使用<xs:restriction>元素来添加对值的约束。

<xs:element name="name">
    <xs:simpleType>
        <xs:restriction base="xs:string">
            <xs:pattern value="[A-Z](\w*)"></xs:pattern>
        </xs:restriction>
    </xs:simpleType>
</xs:element> 

4.7.<subject>元素定义

<subject>元素带有属性main,因此它属于复杂类型。还应注意到<subject>的内容是简单内容simpleContent,因为只包含文本节点。对于<subject>的值,只能取Chinese、Math、English、语文、数学、英语这六个值之一。因为XSD没有内置的Chinese、Math、English、语文、数学、英语枚举数据类型,因此我们必须在<schema>子元素中定义一个新的数据类型(简单类型)。

<xs:simpleType name="subject3">
    <xs:union>
        <xs:simpleType>
            <xs:restriction base="xs:string">
                <xs:enumeration value="Math"></xs:enumeration>
                <xs:enumeration value="Chinese"></xs:enumeration>
                <xs:enumeration value="English"></xs:enumeration>
            </xs:restriction>
        </xs:simpleType>
        <xs:simpleType>
            <xs:restriction base="xs:string">
                <xs:pattern value="数学|语文|英语"></xs:pattern>
            </xs:restriction>
        </xs:simpleType>
    </xs:union>
</xs:simpleType>

在这里有必要说明一下<xs:union>的用法,它指示了一个简单类型的值可以是多个不同的简单类型。<xs:union>可能应用最多的是定义Color的值,它既可以是字符串(“red”),也可以是16进制表示法(#ff0000)。在上面的"subject3"的定义中也说明了一件事,枚举值即可以使用<xs:enumeration>定义,也可以使用正则表达式<xs:pattern>来定义。

下面在<subject>元素中使用新的数据类型subject3

<xs:element name="subject">
    <xs:complexType>
        <xs:simpleContent><!--simpleContent仅仅是用于扩展复杂结构类型-->
            <xs:extension base="subject3">
                <xs:attribute name="main" default="false" type="xs:boolean"></xs:attribute>
            </xs:extension>
        </xs:simpleContent>
    </xs:complexType>
</xs:element>

<xs:extension>元素扩展了简单结构类型subject3,使subject3还包含有一个main属性。

4.8.<students>及其子元素的定义

根据4.1-4.7的说明,很容易举一反三得出<students>及其子元素的定义

students及其子元素定义

5.唯一约束和键引用约束

第4节已经完成了所有的元素定义,但是回头看一下第3节的实例场景,发现我们还有几点要求没有完成:

  1. 老师和学生的名字都不能相同。
  2. 老师中只有一位是校长
  3. 学生的直属指导老师应该引用<teachers>下的老师名字。

5.1.老师和学生的名字都不能相同

在<teachers>下添加unique约束

<xs:element name="teachers">
    <xs:complexType>
        ...
    </xs:complexType>
    <xs:unique name="uni_tname">
        <xs:selector xpath="./s:teacher"></xs:selector>
        <xs:field xpath="./s:name"></xs:field>
    </xs:unique>
</xs:element> 

这里使用了XPath语法,而且XPath中的元素必须使用命名空间。上面这段代码的意思是在<teachers>元素下的所有<teacher>元素的<name>元素值是唯一的。

同理,在<students>下添加unique约束。

<xs:element name="students">
    <xs:complexType>
        ...
    </xs:complexType>
    <xs:unique name="uni_sname">
        <xs:selector xpath="./s:student"></xs:selector>
        <xs:field xpath="./s:name"></xs:field>
    </xs:unique>
</xs:element>

5.2.老师中只有一位是校长

跟5.1类似,只是XPath这个时候引用的是属性节点。

<xs:element name="teachers">
    <xs:complexType>
        ...
    </xs:complexType>
    <xs:unique name="uni_tname">
        <xs:selector xpath="./s:teacher"></xs:selector>
        <xs:field xpath="./s:name"></xs:field>
    </xs:unique>
    <xs:unique name="uni_head">
        <xs:selector xpath="./s:teacher"></xs:selector>
        <xs:field xpath="./@head"></xs:field>
    </xs:unique>
</xs:element>

5.3. 学生的直属指导老师应该引用<teachers>下的老师名字

这里要用到键引用约束,键引用约束与数据库中的主键和外键的概念非常相似。在这个场景中,所有的<teachers><teacher><name>的值为主键,而<students><teacher ref="主键值">中的ref为外键引用主键的值。

首先,我们得声明主键

<xs:element name="school">
    <xs:complexType>
              ...
    </xs:complexType>
    <xs:key name="key_tname"><!--school元素下的所有teachers元素下的所有teacher元素的name元素值作为键-->
        <xs:selector xpath="./s:teachers/s:teacher"></xs:selector>
        <xs:field xpath="./s:name"></xs:field>
    </xs:key>
</xs:element>

接着,引用主键

<xs:element name="school">
    <xs:complexType>
    </xs:complexType>
    <xs:key name="key_tname">
        <xs:selector xpath="./s:teachers/s:teacher"></xs:selector>
        <xs:field xpath="./s:name"></xs:field>
    </xs:key>
    <xs:keyref name="ref_tname" refer="key_tname">
        <xs:selector xpath="./s:students/s:student"></xs:selector>
        <xs:field xpath="./s:teacher/@ref"></xs:field>
    </xs:keyref>
</xs:element>

 

5.4.完整的XSD文档的最终版 

school.xsd
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://www.sin90lzc.com/school" xmlns:s="http://www.sin90lzc.com/school"
    xmlns="http://www.sin90lzc.com/school" elementFormDefault="qualified">

    <xs:simpleType name="subject3">
        <xs:union>
            <xs:simpleType>
                <xs:restriction base="xs:string">
                    <xs:enumeration value="Math"></xs:enumeration>
                    <xs:enumeration value="Chinese"></xs:enumeration>
                    <xs:enumeration value="English"></xs:enumeration>
                </xs:restriction>
            </xs:simpleType>
            <xs:simpleType>
                <xs:restriction base="xs:string">
                    <xs:pattern value="数学|语文|英语"></xs:pattern>
                </xs:restriction>
            </xs:simpleType>
        </xs:union>
    </xs:simpleType>

    <xs:element name="school">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="teachers">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="teacher" minOccurs="1" maxOccurs="unbounded">
                                <xs:complexType>
                                    <xs:sequence>
                                        <xs:element name="name">
                                            <xs:simpleType>
                                                <xs:restriction base="xs:string">
                                                    <xs:pattern value="[A-Z](\w*)"></xs:pattern>
                                                </xs:restriction>
                                            </xs:simpleType>
                                        </xs:element>
                                        <xs:element name="subject">
                                            <xs:complexType>
                                                <xs:simpleContent>
                                                    <xs:extension base="subject3">
                                                        <xs:attribute name="main" default="false" type="xs:boolean"></xs:attribute>
                                                    </xs:extension>
                                                </xs:simpleContent>
                                            </xs:complexType>
                                        </xs:element>
                                    </xs:sequence>
                                    <xs:attribute name="head" type="xs:boolean"></xs:attribute>
                                </xs:complexType>
                            </xs:element>
                        </xs:sequence>
                    </xs:complexType>
                    <xs:unique name="uni_tname">
                        <xs:selector xpath="./s:teacher"></xs:selector>
                        <xs:field xpath="./s:name"></xs:field>
                    </xs:unique>
                    <xs:unique name="uni_head">
                        <xs:selector xpath="./s:teacher"></xs:selector>
                        <xs:field xpath="./@head"></xs:field>
                    </xs:unique>
                </xs:element>
                <xs:element name="students">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="student" maxOccurs="unbounded"
                                minOccurs="1">
                                <xs:complexType>
                                    <xs:sequence>
                                        <xs:element name="name">
                                            <xs:simpleType>
                                                <xs:restriction base="xs:string">
                                                    <xs:pattern value="[A-Z](\w*)"></xs:pattern>
                                                </xs:restriction>
                                            </xs:simpleType>
                                        </xs:element>
                                        <xs:element name="teacher">
                                            <xs:complexType>
                                                <xs:attribute name="ref" type="xs:string">
                                                </xs:attribute>
                                            </xs:complexType>
                                        </xs:element>
                                    </xs:sequence>
                                </xs:complexType>
                            </xs:element>
                        </xs:sequence>
                    </xs:complexType>
                    <xs:unique name="uni_sname">
                        <xs:selector xpath="./s:student"></xs:selector>
                        <xs:field xpath="./s:name"></xs:field>
                    </xs:unique>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
        <xs:key name="key_tname">
            <xs:selector xpath="./s:teachers/s:teacher"></xs:selector>
            <xs:field xpath="./s:name"></xs:field>
        </xs:key>
        <xs:keyref name="ref_tname" refer="key_tname">
            <xs:selector xpath="./s:students/s:student"></xs:selector>
            <xs:field xpath="./s:teacher/@ref"></xs:field>
        </xs:keyref>
    </xs:element>
</xs:schema>

 

6.优化XSD文档

这里直接给出优化后的XSD文档,相信大家的智慧可以轻易看出如何优化XSD文档。

school_opt.xsd
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://www.sin90lzc.com/school" xmlns:s="http://www.sin90lzc.com/school"
    xmlns="http://www.sin90lzc.com/school" elementFormDefault="qualified">

    <xs:simpleType name="subject3">
        <xs:union>
            <xs:simpleType>
                <xs:restriction base="xs:string">
                    <xs:enumeration value="Math"></xs:enumeration>
                    <xs:enumeration value="Chinese"></xs:enumeration>
                    <xs:enumeration value="English"></xs:enumeration>
                </xs:restriction>
            </xs:simpleType>
            <xs:simpleType>
                <xs:restriction base="xs:string">
                    <xs:pattern value="数学|语文|英语"></xs:pattern>
                </xs:restriction>
            </xs:simpleType>
        </xs:union>
    </xs:simpleType>

    <xs:complexType name="subjectType">
        <xs:simpleContent>
            <xs:extension base="subject3">
                <xs:attribute name="main" default="false" type="xs:boolean"></xs:attribute>
            </xs:extension>
        </xs:simpleContent>
    </xs:complexType>

    <xs:simpleType name="nameType">
        <xs:restriction base="xs:string">
            <xs:pattern value="[A-Z](\w*)"></xs:pattern>
        </xs:restriction>
    </xs:simpleType>

    <xs:element name="name" type="nameType"></xs:element>

    <xs:complexType name="teacherType">
        <xs:sequence>
            <xs:element ref="name"></xs:element>
            <xs:element name="subject" type="subjectType"></xs:element>
        </xs:sequence>
        <xs:attribute name="head" type="xs:boolean"></xs:attribute>
    </xs:complexType>

    <xs:complexType name="teachersType">
        <xs:sequence>
            <xs:element name="teacher" minOccurs="1" maxOccurs="unbounded"
                type="teacherType"></xs:element>
        </xs:sequence>
    </xs:complexType>

    <xs:complexType name="refTeacherType">
        <xs:attribute name="ref" type="xs:string"></xs:attribute>
    </xs:complexType>

    <xs:complexType name="studentType">
        <xs:sequence>
            <xs:element ref="name"></xs:element>
            <xs:element name="teacher" type="refTeacherType"></xs:element>
        </xs:sequence>
    </xs:complexType>

    <xs:complexType name="studentsType">
        <xs:sequence>
            <xs:element name="student" maxOccurs="unbounded"
                minOccurs="1" type="studentType">
            </xs:element>
        </xs:sequence>
    </xs:complexType>

    <xs:complexType name="schoolType">
        <xs:sequence>
            <xs:element name="teachers" type="teachersType">
                <xs:unique name="uni_tname">
                    <xs:selector xpath="./s:teacher"></xs:selector>
                    <xs:field xpath="./s:name"></xs:field>
                </xs:unique>
                <xs:unique name="uni_head">
                    <xs:selector xpath="./s:teacher"></xs:selector>
                    <xs:field xpath="./@head"></xs:field>
                </xs:unique>
            </xs:element>
            <xs:element name="students" type="studentsType">
                <xs:unique name="uni_sname">
                    <xs:selector xpath="./s:student"></xs:selector>
                    <xs:field xpath="./s:name"></xs:field>
                </xs:unique>
            </xs:element>
        </xs:sequence>
    </xs:complexType>

    <xs:element name="school" type="schoolType">
        <xs:key name="key_tname">
            <xs:selector xpath="./s:teachers/s:teacher"></xs:selector>
            <xs:field xpath="./s:name"></xs:field>
        </xs:key>
        <xs:keyref name="ref_tname" refer="key_tname">
            <xs:selector xpath="./s:students/s:student"></xs:selector>
            <xs:field xpath="./s:teacher/@ref"></xs:field>
        </xs:keyref>
    </xs:element>
</xs:schema>

 

当然,如果想要school.xml中看到结果,还需要稍微改一下school.xml

<school xmlns="http://www.sin90lzc.com/school" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.sin90lzc.com/school school_opt.xsd">
</school>

 

posted on 2012-05-20 21:34  TimLeung  阅读(1499)  评论(0编辑  收藏  举报

导航