博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

小菜之智能客户端(1)XML,DTD,XSD

Posted on 2008-09-17 11:28  a-peng  阅读(3924)  评论(6编辑  收藏  举报

《还没学好走之前不要跑,还没学好爬之前不要走》

为什么在智能客户端技术的系列文章的第一篇说的是XML、DTD、XSD,而不是具体的智能客户端技术呢?
因为要是没有XML技术就没有智能客户端。

什么是智能客户端?
 智能客户端允许用户的 本地应用程序 通过 Web服务服务器应用程序 交互

Web服务是构建在XML的基础之上。

微软的智能客户端有三个案例源码:FotoVision,IssueVision,TaskVision(这三个源码就是小菜的研究对象)
1、FotoVision使用XML文件存储数据。
2、IssueVision和TaskVision使用MS Sql数据库,Web服务通过读取MS Sql数据库中的数据填充到强类型DataSet,传递给本地应用程序,强类型DataSet会被序列化为XML文件传递。

说了那么多小菜只是想让你明白XML很重要,对于智能客户端XML和XSD你都需要有所了解,DTD的内容如果你的时间不是很充裕可以跳过。

(一)、XML概览
首先让我们下载一个XML编辑器:XMLSpy 2005企业版下载破解方法及其教程
当然你也可以不用XMLSpy编辑器,使用记事本,或者vs2005等等,这取决于你的个人喜爱。
小菜只是想说:如果你使用下XMLSpy,你就会喜欢上它,小菜今天是第一次使用就爱上它了。
小菜不骗人的。:)

1、使用记事本创建一个XML文档(通讯录)Contacts.xml

<Contacts>
    <Person Sex="男">
        <Name>小陈</Nane>
        <Address>
            <Country>中国</Country>
            <Province>福建</Province>
            <City>福州</City>
        </Address>
        <Phone>110</Phone>
        <Email>小陈@gmail.com</Email>
    </Person>
    <Person Sex="女">
        <Name>小林</Name>
        <Address>
            <Country>中国</Country>
            <Province>福建</Province>
            <City>福州</City>
        </Address>
        <Phone>112</Phone>
        <Email>小林@gmail.com</Email>
    </Person>
</Contacts>

XML文档就这么简单,你应该树立起信心。
如果你仔细看一下上面的XML文件你会发现有个错误:<Name>小陈</Nane> 结束标记与开始标记不匹配
小菜想看看XML文档是否真的严谨。

使用IE浏览器打开它。
image 
看来不错,XML设计的很严谨。

2、使用XMLSpy创建上面的XML文档(通讯录)Contacts.xml
上面使用记事本+浏览器,构成了我们的开发平台。不仅麻烦,而且视觉效果很差。

接下来我们来使用一下XMLSpy。
image 
点击Browser会自动提示错误,并且定位到Nane结点。确实方便不是吗?(XMLSpy应该给我颁个奖,这么卖力的帮它宣传)

3、XML也需要约束
XML太方便了,以至于所有人都能打开XML文档修改一番。这也就产生了一个问题,怎么保证XML文档结构良好。

如果阿扁(也就是欠扁的人)想恶作剧一下,修改了XML文档。
 把小陈的Sex修改为<Person Sex="不男不女">
 添加了结点<傻瓜>你是傻瓜吗?</傻瓜> 
image 
在Browser中正常显示出了<Person Sex="不男不女"><傻瓜>你是傻瓜吗?</傻瓜> ,由于你并没有对该XML文档进行相应的约束,所以根本没有错误可言。看来真是糟糕透了,如果被小陈发现了,一定会来个人肉搜索把那该死的阿扁找出来。

可见我们需要对XML文档进行约束,所谓无规矩不成方圆。
约束XML文档的方式主要有两种:
    DTD(Document Type Definition)文档类型定义
    XSD(XML Schema Definition)XML模式定义

(二)、DTD(Document Type Definition)文档类型定义
1、DTD包含在XML中:Contacts.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Contacts [
    <!ELEMENT Contacts (Person*)>
    <!ELEMENT Person (Name, Address, Phone, Email)>
    <!ATTLIST Person Sex (|) "男">
    <!ELEMENT Name (#PCDATA)>
    <!ELEMENT Address (Country, Province, City)>
    <!ELEMENT Country (#PCDATA)>
    <!ELEMENT Province (#PCDATA)>
    <!ELEMENT City (#PCDATA)>
    <!ELEMENT Phone (#PCDATA)>
    <!ELEMENT Email (#PCDATA)>
]>
<Contacts>
    <Person Sex="不男不女">
<傻瓜>你是傻瓜吗?</傻瓜> <Name>小陈</Name> <Address> <Country>中国</Country> <Province>福建</Province> <City>福州</City> </Address> <Phone>110</Phone> <Email>小陈@gmail.com</Email> </Person> <Person Sex="女"> <Name>小林</Name> <Address> <Country>中国</Country> <Province>福建</Province> <City>福州</City> </Address> <Phone>112</Phone> <Email>小林@gmail.com</Email> </Person> </Contacts>

使用XMLSpy点击Browser会提示错误:
image
定位到出错位置:Sex="不男不女" 
在我们的DTD代码中有:<!ATTLIST Person Sex (|) "男">
说明了:Sex为Person元素的属性,为枚举类型,值只能为“男”或“女”,默认值为“男”
改正:将其修改为男。点击Revalidate重新验证。

image
定位到出错位置:<傻瓜>你是傻瓜吗?</傻瓜>
在我们的DTD代码中没有声明该元素,所以提示错误,将其删除。
点击Revalidate重新验证。

现在没有错误了。
接下来就来说说DTD中的基本语法:
<!DOCTYPE Contacts []>
表示根结点为Contacts。注意这是当DTD包含在XML中使用的。
如果XML是引用外部的DTD文件,就不是这样了,下面会说道。

<!ELEMENT Contacts (Person*)>
ELEMENT Contacts 表示声明元素Contacts,这样就允许XML文档中出现<Contacts></Contacts>

Contacts (Person) 表示元素Contacts包含子元素Person,这样就允许XML文档中出现<Contacts><Person></Person></Contacts>

Person* 表示Person可以出现 0次 到 多次
Person+ 表示元素Person可以出现1次 到 多次
Person? 表示元素Person可以出现 0次 到 1次

<!ELEMENT Person (Name, Address, Phone, Email)>
ELEMENT Person 表示声明元素Person

Person (Name, Address, Phone, Email) 表示Person包含元素Name,Address,Phone,Email而且顺序也固定了。

<!ATTLIST Person Sex (|) "男">
ATTLIST Person Sex 表示为Person元素声明属性Sex,为枚举类型,值为:男或者女,默认值为 “男”

 <!ELEMENT Name (#PCDATA)>
声明元素Name,#PCDATA表示:Name元素不包含其他子元素而只包含字符数据的元素。

<!ELEMENT Address (Country, Province, City)>
<!ELEMENT Country (#PCDATA)>
<!ELEMENT Province (#PCDATA)>
<!ELEMENT City (#PCDATA)>
<!ELEMENT Phone (#PCDATA)>
<!ELEMENT Email (#PCDATA)> 
和上面的差不多。

还有一些常用的也附带说下:
比如有个XML文档中有个结点:<image id="image1" src="image1.jpg" width="200" height="300"></image>
要求就省了,直接看对应的dtd代码吧。
<!ELEMENT image EMPTY>
 声明元素image,EMPTY表示元素不含有字符数据或子元素,只有属性。
 如果出现<image>xxx</image>提示错误

<!ATTLIST image id ID #IMPLIED >
 为image元素声明属性id,ID表示属性id为在DTD指定的XML文档中唯一,#IMPLIED表示可以有属性id也可以不出现。
 如果出现<image id="image1"></image><image id="image1"></image>出错。

<!ATTLIST image src CDATA #REQUIRED >
 为image元素声明属性src,CDATA表示src属性的类型为字符数据,#REQUIRED表示src属性是必须的,必须出现。

<!ATTLIST image width CDATA #REQUIRED >
<!ATTLIST image height CDATA #REQUIRED >
和上面的差不多。

常用的差不多都说了,XMLSpy中也带了几个例子,大家有时间也可以看看。了解了上面的内容后,阅读XMLSpy中的例子代码应该不存在问题。
大家可以使用XMLSpy,建个XML文档和DTD,试一下,就都明白了,实践是理解事物最好的途径。

2、XML文档引用外部DTD文件
创建Contacts.dtd文件
<?xml version="1.0" encoding="UTF-8"?>
<!ELEMENT Contacts (Person*)>
<!ELEMENT Person (Name, Address, Phone, Email)>
<!ATTLIST Person Sex (|) "男">
<!ELEMENT Name (#PCDATA)>
<!ELEMENT Address (Country, Province, City)>
<!ELEMENT Country (#PCDATA)>
<!ELEMENT Province (#PCDATA)>
<!ELEMENT City (#PCDATA)>
<!ELEMENT Phone (#PCDATA)>
<!ELEMENT Email (#PCDATA)>
创建Contacts.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Contacts SYSTEM "Contacts.dtd">
<Contacts>
    <Person Sex="男">
        <Name>小陈</Name>
        <Address>
            <Country>中国</Country>
            <Province>福建</Province>
            <City>福州</City>
        </Address>
        <Phone>110</Phone>
        <Email>小陈@gmail.com</Email>
    </Person>
    <Person Sex="女">
        <Name>小林</Name>
        <Address>
            <Country>中国</Country>
            <Province>福建</Province>
            <City>福州</City>
        </Address>
        <Phone>112</Phone>
        <Email>小林@gmail.com</Email>
    </Person>
</Contacts>
只需使用:<!DOCTYPE Contacts SYSTEM "Contacts.dtd"> 就搞定了,DOCTYPE Contacts表示根结点Contacts SYSTEM后跟dtd文件的路径。

(三)、XSD(XML Schema Definition)XML模式定义
XML Schema所起的作用与DTD相同,都是用来约束XML文档的,不过XML Schema有如下三个优势。
1、DTD是用一种与XML不同的语法编写,而XML Schema是使用一种类XML的语言。
2、DTD中的所有声明都是全局声明,而XML Schema既有全局声明也有局部声明。
3、DTD不能对给定的元素或属性的数据类型进行定义,而XML Schema具有一套完整的数据类型系统,它允许对数据类型如整型,时间型或字符串型等进行详细定义。

看了上面的三个优点后,或许你要郁闷了,上面看了DTD原来是浪费时间啊。
虽然DTD可以完全被XML Schema替代,不过了解下DTD也不是什么坏事啊,而且你也没发多少时间,不是吗。

废话不多说了,我们同样为之前的Contacts.xml文档编写Contacts.xsd,做好心理准备噢。
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    elementFormDefault="qualified" 
    attributeFormDefault="qualified">
    <xs:annotation>
        <xs:documentation>This XML Schema is for the Contacts.xml</xs:documentation>
    </xs:annotation>
    <xs:element name="Contacts">
        <xs:complexType>
            <xs:choice minOccurs="0" maxOccurs="unbounded">
                <xs:element ref="Person"/>
            </xs:choice>
        </xs:complexType>
    </xs:element>
    <xs:element name="Person">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="Name" type="xs:string"/>
                <xs:element ref="Address"/>
                <xs:element name="Phone">
                    <xs:simpleType>
                        <xs:restriction base="xs:string">
                            <xs:pattern value="[0-9 \-]*"/>
                        </xs:restriction>
                    </xs:simpleType>
                </xs:element>
                <xs:element name="Email">
                    <xs:simpleType>
                        <xs:restriction base="xs:string">
                            <xs:pattern value="\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*"/>
                        </xs:restriction>
                    </xs:simpleType>
                </xs:element>
            </xs:sequence>
            <xs:attribute name="Sex" default="男">
                <xs:simpleType>
                    <xs:restriction base="xs:string">
                        <xs:enumeration value="男"/>
                        <xs:enumeration value="女"/>
                    </xs:restriction>
                </xs:simpleType>
            </xs:attribute>
        </xs:complexType>
    </xs:element>
    <xs:element name="Address">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="Country" type="xs:string"/>
                <xs:element name="Province" type="xs:string"/>
                <xs:element name="City" type="xs:string"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>
在XMLSpy中点击Schema\WSDL可以看到下图:
image 
结构很清晰吧,这样我们可以很清晰的看到结构。
使用XMLSpy编写XSD,你可以不用手写代码(其实在XMLSpy中手写代码很方便,有智能提示),你可以在上面的视图中进行添加或者删除操作,那属于XMLSpy软件的使用不属于小菜讲的范围,如果感兴趣可以看一下:

XMLSPY教程:该教程将带领您经历一遍XML Schema的创建、XML文件的创建、编辑和转换、在XML与数据库间进行导入导出以及如何把相关文件组织为一个XMLSPY工程的过程。

在Contacts.xml文档中使用
<?xml version="1.0" encoding="UTF-8"?>
<Contacts 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:noNamespaceSchemaLocation="Contacts.xsd">
    <Person Sex="男">
        <Name>小陈</Name>
        <Address>
            <Country>中国</Country>
            <Province>福建</Province>
            <City>福州</City>
        </Address>
        <Phone>110</Phone>
        <Email>小陈@gmail.com</Email>
    </Person>
    <Person Sex="女">
        <Name>小林</Name>
        <Address>
            <Country>中国</Country>
            <Province>福建</Province>
            <City>福州</City>
        </Address>
        <Phone>112</Phone>
        <Email>小林@gmail.com</Email>
    </Person>
</Contacts>
接下来,我们就来分析下Contacts.xsd文件。
1、
<xs:schema
 xmlns:xs=“http://www.w3.org/2001/XMLSchema”
 elementFormDefault="qualified"
 attributeFormDefault="qualified">
</xs:schema>

<schema></schema>是所有XML Schema文件的根结点。

xmlns:xs
=“http://www.w3.org/2001/XMLSchema
表示:XML Schema中使用的元素和数据类型来自于“http://www.w3.org/2001/XMLSchema” 命名空间,它也指定了来自于“http://www.w3.org/2001/XMLSchema”命名空间的元素和数据类型必须附带前缀“xs:”。所以也就有了<xs:schema>。

elementFormDefault
="qualified" 
表示:使用该XML Schema的XML文档所使用的所有元素都必须来自指定的命名空间。

attributeFormDefault="qualified" 同上,把元素换成属性,如果文档中没有用到属性,就无需指定该参数了,默认为unqualified。Contacts.xml文档使用了Sex属性,所以指定。 <Person Sex="男">

2、
<xs:annotation>
 <xs:documentation>This XML Schema is for the Contacts.xml</xs:documentation>
</xs:annotation>
提供注释的功能。

3、
<xs:element name="Contacts">
 <xs:complexType>
  
<xs:choice minOccurs="0" maxOccurs="unbounded">
   <xs:element ref="Person"/>
  </xs:choice>
 </xs:complexType>
</xs:element>

声明一个元素:名称为Contacts,它是复合类型。
元素Contacts由子元素Person构成,Person可以出现 0 到 多次

<xs:element name="Contacts"></xs:element> 声明一个元素名称为Contacts
<xs:complexType></xs:compleyType> Contacts元素为复合类型,即由其它元素组成,这里为由元素Person组成。
<xs:choice minOccurs="0" maxOccurs="unbounded"> minOccurs="0"表示Person至少出现0次,maxOccurs="unbounded"表示Person出现次数无上限。

<xs:element ref="Person" />表示引用Person元素。

4、
<xs:element name="Person">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="Name" type="xs:string"/>
      <xs:element ref="Adress"/>
      <xs:element name="Phone">
        <xs:simpleType>
          <xs:restriction base="xs:string">
            <xs:pattern value="[0-9 \-]*"/>
          </xs:restriction>
        </xs:simpleType>
      </xs:element>
      <xs:element name="Email">
        <xs:simpleType>
          <xs:restriction base="xs:string">
            <xs:pattern value="\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*"/>
           </xs:restriction>
        </xs:simpleType>
      </xs:element>
    </xs:sequence>
    <xs:attribute name="Sex" default="男">
      <xs:simpleType>
        <xs:restriction base="xs:string">
          <xs:enumeration value="男"/>
          <xs:enumeration value="女"/>
         </xs:restriction>
      </xs:simpleType>
    </xs:attribute>
  </xs:complexType>
</xs:element>
<xs:element name="Person"><xs:complexType></xs:complexType></xs:element>
表示:Person元素为复合类型。
<xs:sequence></xs:sequence>指定子元素必须按照特定的顺序出现,Name,Address,Phone,Email
<xs:element name="Name" type="xs:string" />表示:声明元素Name,类型为string
<xs:element ref="Address" />表示引用元素Address

元素Phone比较有意思
<xs:element name="Phone">
  <xs:simpleType>
    <xs:restriction base="xs:string">
      <xs:pattern value="[0-9 \-]*"/>
    </xs:restriction>
  </xs:simpleType>
</xs:element>
元素Phone,它为simpleType,既简单类型。
<xs:restriction base"xs:string"></xs:restriction>表示为Phone定义约束,类型为string,并指定了pattern,
<xs:pattern value="[0-9 \-]" />表示为Phone指定正则表达式,既Phone结点的内容只能是0-9的数字与‘-’构成。
关于正则表达式,有一篇文章写的很好:正则表达式30分钟入门教程

Email和Phone差不多就不解释了。
<xs:attribute name="Sex" default="男">
  <xs:simpleType>
    
<xs:restriction base="xs:string">
      <xs:enumeration value="男"/>
      <xs:enumeration value="女"/>
    
</xs:restriction>
  </xs:simpleType>
</xs:attribute>
为Person声明了属性Sex,默认值为“男”,Sex属性为枚举类型,只能为“男”或者“女”。
我们也可以使用另一种声明方式:

<xs:attribute name="Sex" default="男">
    <xs:simpleType>
        <xs:restriction base="xs:string">
            <xs:pattern value="男|女" />
        </xs:restriction>
    </xs:simpleType>
</xs:attribute>

好了。结尾了。