之前在做项目时时常需要通过一些“小xml”传输或存储一些信息,然后就在读取的时候需要先判断xml数据是否符合要求,包括这次也是这样,不同的是 这次我设计了一个比较复杂的xml,结果读取xml数据 里穿插着各种判断,洋洋洒洒写了一大坨代码。然后我就想不是有schema这种xml描述语言吗,那应该也可以在代码里用它进行校验xml…
在实现使用schema校验xml这个目标前,你首先得自己会根据自己的xml要求格式写出对应的schema,我认为写schema占实现校验目标的七成,剩下的三成才是使用代码去校验。因为一旦有schema,通过框架去校验就是一段很固定的写法了,这也就是为什么要提议使用schema,可以大大减少不必要的重复代码工作,而可以专心于读取数据处,处理关键逻辑。
一、几个重要的标头
xmlns:xs="http://www.w3.org/2001/XMLSchema"
显示 schema 中用到的元素和数据类型来自命名空间 “http://www.w3.org/2001/XMLSchema“。同时它还规定了来自命名空间 “http://www.w3.org/2001/XMLSchema” 的元素和数据类型应该使用前缀xs:
xmlns="http://www.runoob.com"
指出该文件的默认命名空间,在文档中所有的名字前面如果没有前缀的,就是由默认命名空间进行定义和解析的。使用默认命名空间,可以不加空间前缀。
targetNamespace="http://www.runoob.com"
标明节点下面所定义的类型都属于这个命名空间。使用targetNamespace命名空间下的元素必须要加前缀。
elementFormDefault="qualified"
表示任何xml中使用本xsd中声明的元素必须使用命名空间
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
这句话引用的这个命名空间是干什么呢(它和xmlns:xs
一样都是一个正常的引用语句,xs
,xi
都是一个别名),他和下面这句话有关:因为下面这句话需要指出你应用的命名空间的实际的文档在放在哪。这个属性就是schemaLocation,而这个属性被定义在http://www.w3.org/2001/XMLSchema-instance
中
xsi:schemaLocation="http://www.runoob.com note.xsd"
关于xmlns
和targetNamespace
的意义区分
可重用元素的使用与命名空间
xmlns与targetNamespace
关于XML Schema命名空间中已经有xmlns却还要targetnamespace的理解
我的理解:
一个元素节点添加了targetNamespace,那么这个节点以及节点之下就是用来定义这个节点。如果要用这个节点 下的元素,那么就得加上这个节点的targetNamespace,而且还必须j加上xmlns:xxx=ttt(这里的ttt代表targetNamespace,xxx代表targetNamespace的别名,然后在调用的地方使用xxx:nnn),哪怕是在这个节点之中“调用”。所以,如果你写的这个xsd并不打算让别人调用,那么就别写什么targetNamespace,这只会给你增加麻烦:自己定义的类型,同一个文件内调用还得加上命名空间,多麻烦
二、一些常用的知识
简易类型:
- xs:string
- xs:decimal【十进制数】
- xs:integer【也有int类型】
- xs:boolean
- xs:date
- xs:time
字符串类型是我们在xml里最常用的类型,但有时候 我只希望用户填写固定的某几个字符串,实现“枚举类型”,那么可以这么写
<xs:simpleType name="carType">
<xs:restriction base="xs:string">
<xs:enumeration value="Audi"/>
<xs:enumeration value="Golf"/>
<xs:enumeration value="BMW"/>
</xs:restriction>
</xs:simpleType>
这就实现了个简单的枚举类型,首先是string类型的,然后限定在“Audi”,“Golf”,“BMW”里面的其中一个。
下面是常用的几种对字符串限定的方式
- enumeration (布尔数据类型无法使用此约束*)
- length (布尔数据类型无法使用此约束)
- maxLength (布尔数据类型无法使用此约束)
- minLength (布尔数据类型无法使用此约束)
三、注意几个问题
- 如果你想 定义一个
类型
,然后在其他地方重复引用,不要定义成元素
了
正确:
<xs:element name="product" type="prodtype"/>
<xs:complexType name="prodtype">
<xs:attribute name="prodid" type="xs:positiveInteger"/>
</xs:complexType>
错误:
<xs:element name="product" type="prodtype"/>
<xs:element name="product" type="prodtype"/>
<xs:complexType>
<xs:attribute name="prodid" type="xs:positiveInteger"/>
</xs:complexType>
</xs:element>
- 当使用all指示器的时候,minOccurs可以选0或1,maxOccurs只能选1。即任意排序里的“子元素组”只能有1组。否则会报错的!
- Order指示器和Group指示器的默认子元素个数都是1,你如果想允许xml里可以写多个元素,只能自己修改minOccurs、maxOccurs
- 为了避免用户在填写xml的内容是出现不必要的空格,我建议string类型改为token类型,它可以去掉前后空格及tab(很多网上的解释感觉很有歧义:也不知道这个类型的意思是可以去掉空格,还是不能包含空格 否则报错;试验一下就知道了,在填写xml的string类型时加上空格tab并不报错,所以它的意思是帮你在读取的时候过滤掉空格)
四、使用代码校验xml
设计好xml,写好schema后就可以写程序进行校验了
有个问题:xml可以用schema来校验,那schema自己的对错怎么检验呢
其实很简单,把schema粘到eclipse里就行了,对于语法上的错误,比如一开始说的那个,使用了targetNamespace,结果在下面引用了 这个schema里定义的类型,还没有加 命名空间。写到eclipse里就会报错了。当然,eclipse也只能检验语法错误,“逻辑”错误谁也没辙。
- 如果想写完schema就看看效果, 就把xml和他对应的sxd文件都放在eclipse的同一个工程的同一个目录下,然后在xml的标头里加上
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ddddd.xsd"
还记得一开始说的XMLSchema-instance里的schemaLocation
属性吗,它需要写成这样
xsi:schemaLocation="http://www.runoob.com note.xsd"
前一个值是命名空间名称,后一个才是schema所处的真正位置。而它的同胞noNamespaceSchemaLocation
从名字上就可以看出,这个不需要命名空间名称,而只需要真实地址就行了,所以我直接写上schema的位置,在eclipse里就可以“实时”看看效果了,xml格式不符合xsd要求的时候,xml就会报错。可以通过这种简单的方式对xml和schema文件进行相互检验
- 最终还是要落实到代码上
因为代码很固定,即使使用的框架不同,但最终都是一段差不多固定的代码,就不再重复,给两个还不错的链接:
Java通过XML Schema校验XML
Schema和XMLErrorHandler问题
就说一点,和第二个链接有关:我们是否需要把xml校验的错误结果给出很“人性化,通俗”的错误反馈?
我的个人想法:既然我们用了xml来传输数据,那么对方一般就不是什么都不懂的“外行人”。对于“程序对程序”的传输,传输方就根本不应该出现 xml格式错误的问题:我们既然写了schema,就是对外公开的,你在发送之前就得保证自己是按照schema的格式要求写的。 对于“人对程序”,把这样的错误信息:
<errors>
<error column="35" line="17" systemID="file:///E:/workspace-jzgk/loginUtil/web-enter.xml">cvc-enumeration-valid: Value 'ID1' is not facet-valid with respect to enumeration '[ID, NAME, CLASS, LINK_TEXT, PARTIAL_LINK_TEXT, XPATH, CSS_SELECTOR]'. It must be a value from the enumeration.</error>
<error column="35" line="17" systemID="file:///E:/workspace-jzgk/loginUtil/web-enter.xml">cvc-type.3.1.3: The value 'ID1' of element 'fix-type' is not valid.</error>
</errors>
反馈给用户,基本已经够 一个会配置xml的人理解是什么错误了,再解释 也无非把那几个英文单词翻译一下