簡介
XML 是一種受到廣泛支持的 Internet 標准,用於以一種特殊的方式編碼結構化數據。實際上,以 XML 編碼的數據可以通過任何編程語言解碼,人們甚至可以使用標准的文本編輯器來閱讀或編寫 XML 數據。許多應用程序,尤其是兼容現代標准的 Web 瀏覽器,可以直接處理 XML 數據。
作為一個基於文本的標准,XML 非常適於在客戶機和服務器系統之間交換數據。大部分數據(文件路徑、描述、地址、名稱等)已經是基於文本的數據,而整數、浮點數字和日期等數據可以在這些數據格式和字符串格式之間來回輕松轉換。
遺憾的是,要將某些數據(比如 XHTML 或 XML 標記)包含在 XML 文檔內則比較麻煩。將標記放進 XML 元素的一種方法是將某些標記字符(小於 [<]、大於 [>] 和與字符 [&])替換為它們的對等實體(分別為 <、> 和 &)。這種方法擴展了數據並使數據極其不適合人們閱讀,更別提在文本編輯器里手動編寫 XML 時必須轉換標記給您帶來的憤怒了。
更好的解決方案可能是將數據直接放到您的 XML 文檔中,那就是 XML 的 CDATA 區域發揮作用的時候了。
CDATA 是何物?
XML 文檔中的文本通常解析為字符數據,或者(按照文檔類型定義術語)稱為 PCDATA。XML 的特殊字符(&、< 和 >)在 PCDATA 中可以識別,並用於解析元素名稱和實體。CDATA(字符數據)區域被解析器視為數據塊,從而允許您在數據流中包含任意字符。
如果您曾經嘗試過將一些 HTML 或 XML 放到一個 XML 文檔(也許作為文檔資料)中,那么只要您試圖包含一個示例,就會遇到這個問題。清單 1 展示了一個簡單的段落示例,包含一些強調文本。
清單 1. 一個 sample 元素中的一些樣例 XHTML
1 <?xml version="1.0" encoding="UTF-8"?> 2 <sample> 3 <description> 4 Paragraphs can include emphasized text. 5 </description> 6 7 <example> 8 <p>The pug snoring on the couch next to me is 9 <em>extremely</em> cute.</p> 10 </example> 11 </sample>
當您想顯示標記時,您將遇到極大的麻煩(參見 清單 2)。
清單 2. 顯示標記的樣例 XHTML
1 <?xml version="1.0" encoding="UTF-8"?> 2 <sample> 3 <description> 4 Paragraphs can include emphasized text. 5 </description> 6 7 <example> 8 <p>The pug snoring on the couch next to me is 9 <em>extremely<em> cute.</p> 10 </example> 11 </sample>
將這個樣例標記包含在一個 CDATA 區域中允許您原樣編寫標記,無需 XML 解析器試圖將其解釋為一個包含 <em> 元素的 <p> 元素。如果您的 XML 正在針對一個 DTD 或 XML 模式進行驗證,則這是必須的(除非這些元素實際存在於 DTD 或 XSD 中且可以包含在文檔中該位置)。參見 清單 3。
清單 3. 使用 CDATA 來保護樣例
1 <?xml version="1.0" encoding="UTF-8"?> 2 <sample> 3 <description> 4 Paragraphs can include emphasized text. 5 </description> 6 7 <example> 8 <![CDATA[<p>The pug snoring on the couch next to me is 9 <em>extremely</em> cute</p>]]> 10 </example> 11 </sample>
使用 CDATA
如 清單 3 中的簡單示例所示,CDATA 區域的起始標記是一個特殊的序列 <![CDATA[,結束標記是 ]]> 序列。這些標記之間的任何內容都將原封不動地通過 XML 解析器。有些開發平台擁有特殊的 CDATA 對象(比如 XML DOM 中的 CDATASection)來表示 CDATA 區域中的內容,但其他平台將其作為更通用的組件提供,通常是 XML 文本節點。不管是哪種情況,CDATA 區域的內容都將不經修改就可用。
即使 XML 通常允許有空白區域,但 ]]> 區域結束標記不能包含任何空格和換行符。
XHTML 中的 CDATA
如果您已經看到過許多包含嵌入式 JavaScript 的 Web 頁面,那么您也就看到了 CDATA 的實際應用。您將經常看到類似於 清單 4 的內容。
清單 4. XHTML 的 <script> 元素中的 CDATA
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 3 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 4 <html xmlns="http://www.w3.org/1999/xhtml"> 5 <head> 6 <meta http-equiv="Content-Type" 7 content="application/xhtml+xml;charset=utf-8"/> 8 <title>CDATA Section in Action</title> 9 <script type="text/javascript"> 10 // <![CDATA[ 11 function nowWeAreSafe( x, y, z ) { 12 // Without the CDATA section, these would cause 13 // parsing errors: 14 if( x < y && y > z ) { 15 return y--; 16 } 17 return 0; 18 } 19 // ]]> 20 </script> 21 </head> 22 <body> 23 ... 24 </body> 25 </html>
<script> 元素中的 JavaScript 開始於一個包含 CDATA 區域的開始標記的注釋,結束於一個關閉這個 CDATA 區域的注釋。這種方法看起來沒有意義,只會使您的 XHTML 和 JavaScript 更雜亂,除非您意識到:沒有這個 CDATA 區域,您的腳本將通過 Web 瀏覽器的 XHTML 解析器運行。
這通常不會導致問題出現,除非您非常 “幸運”,但這肯定會導致解析器錯誤,這種解析器錯誤會導致令人迷惑、難以調試的呈現錯誤。這是什么原因呢?
如您所料,<、> 和 & 字符可以標記為元素或實體(或者游離的標記字符)。而且,雙短橫線(--)序列可以視為一個 XHTML 塊的意外開始(或結束)。事實上,那就是為什么應該將嵌入式腳本封裝到 CDATA 區域而不是 XML 注釋中的原因 — 注釋太脆弱了。
CDATA 有時也出現在內聯 <style> 元素中,盡管這不太常見(參見 清單 5)。
清單 5. CDATA 阻止 <style> 元素中的解析錯誤
1 <style type="text/css"> 2 /* <![CDATA[ */ 3 body { 4 background-image: 5 url("marble.png?width=300&height=300") 6 } 7 /* ]]> */ 8 </style>
再次注意 CDATA 標記是如何隱藏在特定於語言的注釋中的,這樣它們就不會迷惑客戶機 Web 瀏覽器中的 CSS 解析器。
CDATA 的局限
顯然,CDATA 區域很有用;但與其他所有好東西一樣,它也有幾個應該牢記的限制:
瀏覽器通常不是 XML 解析器
瀏覽器不會可靠地處理 HTML 或 XHTML 中的 CDATA(如果有的話)。CDATA 區域可以出現在 XHTML 中的任意位置(就像在任何 XML 應用程序中一樣),但在實踐中,這些 CDATA 區域通常會被忽略。它們的內容要么丟失(CDATA 區域從正常的 DOM 中消失),要么顯示為文本,並帶有一些游離的標記字符。
為了查看這種效果,請檢查一個頁面,該頁面顯示樣例段落,帶有可見標記(使用實體)的樣例段落,並嘗試使用 CDATA 顯示帶有可見標記的樣例段落。這個 XHTML 頁面源代碼如 清單 6 所示。
清單 6. 嘗試在 XHTML 中使用 CDATA
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 3 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 4 <html xmlns="http://www.w3.org/1999/xhtml"> 5 <head> 6 <meta http-equiv="Content-Type" content="application/xhtml+xml;charset=utf-8"/> 7 <title>CDATA Section in Action</title> 8 </head> 9 <body> 10 <h1>CDATA Section in Action</h1> 11 12 <p> 13 A sample paragraph: 14 </p> 15 16 <p>The pug snoring on the couch next to me is <em>extremely</em> 17 cute.</p> 18 19 <p> 20 Markup version: 21 </p> 22 23 <p id="no1"> 24 <p>The pug snoring on the couch next to me is <em>extremely</em> cute.</p> 25 </p> 26 27 <p> 28 CDATA version: 29 </p> 30 31 <p id="no2"> 32 Uh, 33 <![CDATA[<p>The pug snoring on the couch next to me is <em>extremely</em> cute.</p>]]> 34 where? 35 </p> 36 37 <p> 38 Wait, what? 39 </p> 40 41 </body> 42 </html>
....
盡管當 XHTML 文檔包含 CDATA 區域時瀏覽器的表現不正常,但瀏覽器的確能夠正確處理通過 Ajax 加載的 XML 文檔中的 CDATA 區域。如果某個瀏覽器不能正確處理,那么人們將認為該瀏覽器的 XML 解析器 “不規范”,無情地嘲笑它,然后給它貼上 “肆意破壞 Ajax 規范” 的標簽。
區域結束標記仍然特殊
即使您能夠將任何內容放到一個 CDATA 區域中,這個區域結束標記的序列(]]>)仍然被認為是特殊的。CDATA 區域絕對不能嵌套。如果 XML 解析器讀到這個序列,就認為您的 CDATA 區域結束,當解析器遇到真正的區域結尾時,可能會發出一條解析錯誤消息。
換句話說,XML(或 XHTML)解析器不允許在一個 CDATA 區域之內使用 <![CDATA[,因為解析器將忽略除區域結束標記 ]]> 之外的其他標記字符(參見 清單 7)。
清單 7. 這是無效 XML,您不能嵌套 CDATA 區域
1 <?xml version="1.0" encoding="UTF-8"?> 2 <sample> 3 <description> 4 You can't nest CDATA sections. 5 </description> 6 7 <example> 8 <![CDATA[You want a <![CDATA[ ]]> inside your 9 example? No, this is wrong.]]> 10 </example> 11 </sample>
如果您需要將區域結束標記放到 CDATA 區域中,那么該怎么辦呢?您需要將它分割為兩個 CDATA 區域(見 清單 8)。
清單 8. 將區域結束標記放到 CDATA 區域中的正確方法
1 <?xml version="1.0" encoding="UTF-8"?> 2 <sample> 3 <description> 4 Split up the section end. 5 </description> 6 7 <example> 8 <![CDATA[You want a ]]]]><![CDATA[> 9 inside your example? Do it this way.]]> 10 </example> 11 </sample>
也就是說,將您的數據中的任何 ]]> 替換為 ]]]]><![CDATA[>,以便序列中最后的 > 遠離這些中括號。解析器正在特意尋找作為三個字符序列的 ]]>,通過分割它,您打破了這個序列。
不錯,]]]]><![CDATA[> 是一大塊令人畏懼的標記。幸運的是,這種情況並不經常出現。
區域結束標記仍然是文本
即使 CDATA 區域的內容完好無損地通過您的解析器,它們仍需是有效的 XML 數據字符,正如文檔的字符編碼所規定的一樣。使用類似於 UTF-8 之類的編碼方式,您可以對數據使用廣泛的字符,但它並非完全是 8 位的。
所有所謂的控制字符(那些 16 進制值低於 0x20 的字符,空格字符)可能會導致您的解析器因為一個無效的標記錯誤而停止工作。您不能將任何數據隨意地塞到一個 CDATA 區域中同時還擁有一個有效的文檔。
大小問題
當您使用 CDATA 區域在 XML 中添加數據塊時,最后需要考慮的問題是大小。如果您通過 Web 服務來提供 XML 文件,確保您的客戶機應用程序能夠處理潛在的大型數據傳輸,當數據通過 3G 連接緩慢傳輸時不會導致超時或堵塞它們的用戶界面。
反過來也一樣;確保您的服務器能夠接受來自發送 XML 數據的客戶機的大型上游傳輸。Web 服務器(特別是 Windows® 平台上的 IIS)通常具有相當小的上傳限制,不能幫助阻止拒絕服務攻擊。像這樣從瀏覽器發送大量數據容易出錯(例如,用戶可能會認為傳輸已經崩潰而取消傳輸,出現這種情況該怎么辦呢?),這樣很可能會鎖定服務器和客戶機上的有用資源。
同樣,根據您正在做的工作,您需要記住:許多人正在使用移動平台,其他人也可能被堵塞在撥號連接上(仍然如此!),假定您的應用程序在您的 LAN 外部工作。
即使您的應用程序不是那樣設計的,有的人還是會試圖通過他們 iPhone 手機上的撥號 VPN 連接使用它,他們會抱怨您的應用程序速度太慢,而不是他們愚蠢的生活選擇!
在 XML 中存儲二進制數據
當您的確需要在 XML 文檔中包含二進制數據時,您需要確保這些數據不會給 XML 解析器帶來麻煩。如果這些數據碰巧是文本,您只需將它們放到 CDATA 區域中,但真正的二進制數據需要以一種安全且可恢復的方式編碼。
幸運的是,MIME 標准定義了一種受到廣泛支持的、安全的編碼模式:base64。經過 base64 編碼的二進制數據大約是原來大小的 137%,因此您需要在額外的存儲空間(和少許處理吞吐量)與在 XML 文檔中嵌入二進制數據的能力之間找到平衡。
通常,您需要在 XML 文檔中標明原始文件和已編碼文件的文件名,如 清單 9 所示。
清單 9. XML 文檔內的 base64 編碼文件示例
1 <?xml version="1.0" encoding="UTF-8"?> 2 <sample> 3 <description> 4 An embedded image file. 5 </description> 6 7 <image name="stop.png" encoding="base64" 8 source="FamFamFam" 9 href="http://www.famfamfam.com/lab/icons/silk/"> 10 iVBORw0KGgoAAAANSUhEUgAAABAAAAAQ 11 CAYAAAAf8/9hAAAABGdBTUEAAK/INwWK 12 6QAAABl0RVh0U29mdHdhcmUAQWRvYmUg 13 SW1hZ2VSZWFkeXHJZTwAAAJOSURBVDjL 14 pZI9T1RBFIaf3buAoBgJ8rl6QVBJVNDC 15 ShMLOhBj6T+wNUaDjY0WmpBIgYpAjL/A 16 ShJ+gVYYYRPIony5IETkQxZ2770zc2fG 17 YpflQy2MJzk5J5M5z/vO5ESstfxPxA4e 18 rL4Zuh4pLnoaiUZdq7XAGKzRJVbIBZ3J 19 PLJaD9c/eCj/CFgZfNl5qK5q8EhTXdxx 20 LKgQjAFr0NK0ppOpt9n51D2gd2cmsvOE 21 lVcvOoprKvuPtriNzsY8rH+H0ECoQEg4 22 WklY1czP8akZby51p6G3b6QAWBl43llS 23 VTlUfuZE3NmYh9Vl0HkHSuVq4ENFNWFd 24 C+uJ5JI/9/V2Y//rkShA1HF6yk/VxJ0f 25 07CcgkCB7+fSC8Dzcy7mp4l9/khlUzwe 26 caI9hT+wRrsOISylcsphCFLl1RXIvBMp 27 YDZJrKYRjHELACNEgC/KCQQofWBQ5nuV 28 64UAP8AEfrDrQEiLlJD18+p7BguwfAoB 29 UmKEsLsAGZSiFWxtgWWP4gGAkuB5YDRW 30 ylKAKIDJZBa1H8Kx47C1Cdls7qLnQTZf 31 fQ+20lB7EiU1ent7sQBQ6+vdq2PJ5dC9 32 ABW1sJnOQbL5Qc/HpNOYehf/4lW+jY4v 33 h2tr3fsWafrWzRtlDW5f9aVzjUVj72Fm 34 CqzBypBQCKzbjLp8jZUPo7OZyYm7bYkv 35 w/sAAFMd7V3lp5sGqs+fjRcZhVYKY0xu 36 pwysfpogk0jcb5ucffbbKu9Esv1Kl1N2 37 +Ekk5rg2DIXRmog1Jdr3F/Tm5mO0edc6 38 MSP/CvjX+AV0DoH1Z+D54gAAAABJRU5E 39 rkJggg== 40 </image> 41 </sample>
在機器生成的 XML 文檔中,您可以省略空白,並且無需換行字符就可以運行整個 base64 編碼的文件。
避免問題
處理 XML 中的二進制數據的最好方法是完全避免這種數據。正如您在 HTML 中所看到的,以一種標准方式引用一個外部文件效果很好。當您有某種途徑來使客戶機應用程序獲取外部文件時,這是一個極好的選擇。對於 HTML,瀏覽器只是發出另一個 HTTP 請求,獲取通過 <img> 這樣的元素包含的數據。
通過不在 XML 中直接包含二進制數據,您避免了潛在的文本編碼浪費,並有可能實現其他增強功能,比如多數人喜愛的 Web 瀏覽器中的圖像緩存。
結束語
您可以使用 XML 的 CDATA 區域(開始標記為 <![CDATA,結束標記為 ]]>)來使您文檔中的一部分遠離解析器。CDATA 區域中的數據將原樣通過解析器,進去時是什么文本,出來時還是什么文本;盡管您需要通過停止並重新開始 CDATA 區域來保護任何 ]]> 序列。
即使您不能在 XHTML 文檔中利用 CDATA 的優勢,XML 仍然在瀏覽器和常規編程平台中受到很好的支持。使用 CDATA 來將標記數據直接嵌入 XML 文檔免除了編碼數據的煩惱,但是您要小心翼翼,並考慮客戶機和服務器上(潛在)大型數據傳輸的影響。
當您需要在 XML 文檔中存儲二進制數據時,可以使用標准的 MIME base64 這樣的文本編碼,盡管引用一個外部文件可能是個更好的主意。
轉自:http://club.topsage.com/thread-584438-1-1.html