java通過schema校驗xml

發現存在發表於2017-07-28

之前在做專案時時常需要通過一些“小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"

關於xmlnstargetNamespace的意義區分
可重用元素的使用與名稱空間
xmlns與targetNamespace
關於XML Schema名稱空間中已經有xmlns卻還要targetnamespace的理解
我的理解:
一個元素節點新增了targetNamespace,那麼這個節點以及節點之下就是用來定義這個節點。如果要用這個節點 下的元素,那麼就得加上這個節點的targetNamespace,而且還必須j加上xmlns:xxx=ttt(這裡的ttt代表targetNamespace,xxx代表targetNamespace的別名,然後在呼叫的地方使用xxx:nnn),哪怕是在這個節點之中“呼叫”。所以,如果你寫的這個xsd並不打算讓別人呼叫,那麼就別寫什麼targetNamespace,這隻會給你增加麻煩:自己定義的型別,同一個檔案內呼叫還得加上名稱空間,多麻煩

二、一些常用的知識

  1. 簡易型別:

    • xs:string
    • xs:decimal【十進位制數】
    • xs:integer【也有int型別】
    • xs:boolean
    • xs:date
    • xs:time
  2. 字串型別是我們在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 (布林資料型別無法使用此約束)

三、注意幾個問題

  1. 如果你想 定義一個型別,然後在其他地方重複引用,不要定義成元素
    正確:
<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>
  1. 當使用all指示器的時候,minOccurs可以選0或1,maxOccurs只能選1。即任意排序裡的“子元素組”只能有1組。否則會報錯的!
  2. Order指示器和Group指示器的預設子元素個數都是1,你如果想允許xml裡可以寫多個元素,只能自己修改minOccurs、maxOccurs
  3. 為了避免使用者在填寫xml的內容是出現不必要的空格,我建議string型別改為token型別,它可以去掉前後空格及tab(很多網上的解釋感覺很有歧義:也不知道這個型別的意思是可以去掉空格,還是不能包含空格 否則報錯;試驗一下就知道了,在填寫xml的string型別時加上空格tab並不報錯,所以它的意思是幫你在讀取的時候過濾掉空格)

四、使用程式碼校驗xml

設計好xml,寫好schema後就可以寫程式進行校驗了
有個問題:xml可以用schema來校驗,那schema自己的對錯怎麼檢驗呢
其實很簡單,把schema粘到eclipse裡就行了,對於語法上的錯誤,比如一開始說的那個,使用了targetNamespace,結果在下面引用了 這個schema裡定義的型別,還沒有加 名稱空間。寫到eclipse裡就會報錯了。當然,eclipse也只能檢驗語法錯誤,“邏輯”錯誤誰也沒轍。

  1. 如果想寫完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檔案進行相互檢驗

  1. 最終還是要落實到程式碼上
    因為程式碼很固定,即使使用的框架不同,但最終都是一段差不多固定的程式碼,就不再重複,給兩個還不錯的連結:
    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的人理解是什麼錯誤了,再解釋 也無非把那幾個英文單詞翻譯一下

相關文章