之前在做項目時時常需要通過一些“小xml”傳輸或存儲一些資訊,然後就在讀取的時候需要先判斷xml資料是否符合要求,包括這次也是這樣,不同的是 這次我設計了一個比較複雜的xml,結果讀取xml資料 裡穿插着各種判斷,洋洋灑灑寫了一大坨代碼。然後我就想不是有schema這種xml描述語言嗎,那應該也可以在代碼裡用它進行校驗xml…
在實作使用schema校驗xml這個目标前,你首先得自己會根據自己的xml要求格式寫出對應的schema,我認為寫schema占實作校驗目标的七成,剩下的三成才是使用代碼去校驗。因為一旦有schema,通過架構去校驗就是一段很固定的寫法了,這也就是為什麼要提議使用schema,可以大大減少不必要的重複代碼工作,而可以專心于讀取資料處,處理關鍵邏輯。
一、幾個重要的标頭
顯示 schema 中用到的元素和資料類型來自命名空間 “http://www.w3.org/2001/XMLSchema“。同時它還規定了來自命名空間 “http://www.w3.org/2001/XMLSchema” 的元素和資料類型應該使用字首
xs:
指出該檔案的預設命名空間,在文檔中所有的名字前面如果沒有字首的,就是由預設命名空間進行定義和解析的。使用預設命名空間,可以不加空間字首。
标明節點下面所定義的類型都屬于這個命名空間。使用targetNamespace命名空間下的元素必須要加字首。
表示任何xml中使用本xsd中聲明的元素必須使用命名空間
這句話引用的這個命名空間是幹什麼呢(它和
xmlns:xs
一樣都是一個正常的引用語句,
xs
,
xi
都是一個别名),他和下面這句話有關:因為下面這句話需要指出你應用的命名空間的實際的文檔在放在哪。這個屬性就是schemaLocation,而這個屬性被定義在
http://www.w3.org/2001/XMLSchema-instance
中
關于
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
屬性嗎,它需要寫成這樣
前一個值是命名空間名稱,後一個才是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: The value 'ID1' of element 'fix-type' is not valid.</error>
</errors>
回報給使用者,基本已經夠 一個會配置xml的人了解是什麼錯誤了,再解釋 也無非把那幾個英文單詞翻譯一下