Có cách nào để thoát mã thông báo kết thúc CDATA trong xml không?


129

Tôi đã tự hỏi liệu có cách nào để thoát mã thông báo kết thúc CDATA ( ]]>) trong phần CDATA trong tài liệu xml không. Hoặc, nói chung, nếu có một chuỗi thoát nào đó để sử dụng trong CDATA (nhưng nếu nó tồn tại, tôi đoán có lẽ chỉ có nghĩa là thoát khỏi mã thông báo bắt đầu hoặc kết thúc, dù sao đi nữa).

Về cơ bản, bạn có thể có mã thông báo bắt đầu hoặc kết thúc được nhúng trong CDATA và yêu cầu trình phân tích cú pháp không diễn giải nó mà chỉ coi nó như một chuỗi ký tự khác.

Có lẽ, bạn chỉ nên cấu trúc lại cấu trúc xml hoặc mã của mình nếu bạn thấy mình đang cố gắng làm điều đó, nhưng mặc dù tôi đã làm việc với xml hàng ngày trong 3 năm qua và tôi chưa bao giờ gặp phải vấn đề này, Tôi đã tự hỏi nếu nó có thể. Chỉ tò mò thôi.

Biên tập:

Khác với việc sử dụng mã hóa html ...


4
Đầu tiên, tôi chấp nhận câu trả lời là đúng nhưng lưu ý: Không có gì ngăn cản ai đó mã hóa >như >trong CData để đảm bảo nhúng ]]>sẽ không được phân tích cú pháp như CDEnd. Điều đó đơn giản có nghĩa là nó bất ngờ và &phải được mã hóa ĐẦU TIÊN &để dữ liệu có thể được giải mã chính xác. Người dùng của tài liệu cũng phải biết để giải mã CData này. Không có gì lạ khi một phần của mục đích của CData là chứa nội dung mà một người tiêu dùng cụ thể hiểu cách xử lý. Một CData như vậy không thể được dự kiến ​​sẽ được giải thích đúng bởi bất kỳ người tiêu dùng chung chung nào.
nix

1
@nix, CDATA chỉ cung cấp một cách rõ ràng để khai báo nội dung nút văn bản sao cho các mã thông báo ngôn ngữ bên trong (trừ]]>) không bị phân tích cú pháp. Nó đặc biệt không mở rộng các tham chiếu thực thể như & gt; vì lý do này, vì vậy trong một khối CDATA, điều đó chỉ có nghĩa là bốn ký tự đó, không phải '>'. Để đặt nó trong phối cảnh: trong thông số xml, tất cả nội dung văn bản được gọi là "cdata", không chỉ các chuỗi này ("dữ liệu ký tự"). Ngoài ra, đó không phải là về các tác nhân tiêu thụ cụ thể. (Một thứ như vậy tồn tại mặc dù - hướng dẫn xử lý (<? Hướng dẫn đích?>).
Dấu chấm phẩy

. ]]> không thực sự được thiết kế cho mục đích đó.)
Dấu chấm phẩy

1
@Semicolon CDATAđược thiết kế để cho phép mọi thứ : chúng được sử dụng để thoát khỏi các khối văn bản có chứa các ký tự mà nếu không nó sẽ được công nhận là đánh dấu Điều đó CDATAcũng ngụ ý vì nó cũng là đánh dấu. Nhưng, trên thực tế, bạn không cần mã hóa kép mà tôi ngụ ý. ]]&gt;là một phương tiện mã hóa được chấp nhận CDEndtrong vòng a CDATA.
nix

Đúng, bạn sẽ không cần mã hóa kép - nhưng bạn vẫn cần đại lý có kiến ​​thức đặc biệt, vì trình phân tích cú pháp sẽ không phân tích cú pháp & gt; như>. Đó là những gì bạn có ý nghĩa, tôi nghĩ? Rằng bạn có thể thay thế chúng khi bạn thấy phù hợp, sau khi phân tích cú pháp?
Dấu chấm phẩy

Câu trả lời:


141

Rõ ràng, câu hỏi này hoàn toàn là học thuật. May mắn thay, nó có một câu trả lời rất chắc chắn.

Bạn không thể thoát khỏi chuỗi kết thúc CDATA. Quy tắc sản xuất 20 của đặc tả XML khá rõ ràng:

[20]    CData      ::=      (Char* - (Char* ']]>' Char*))

EDIT: Quy tắc sản phẩm này có nghĩa đen là "Phần CData có thể chứa bất cứ thứ gì bạn muốn NHƯNG chuỗi ']]>'. Không có ngoại lệ."

EDIT2: Phần tương tự cũng đọc:

Trong phần CDATA, chỉ chuỗi CDEnd được nhận dạng là đánh dấu, do đó dấu ngoặc vuông và ký hiệu góc trái có thể xảy ra ở dạng nghĩa đen của chúng; họ không cần (và không thể) được thoát bằng " &lt;" và " &amp;". Phần CDATA không thể làm tổ.

Nói cách khác, không thể sử dụng tham chiếu thực thể, đánh dấu hoặc bất kỳ hình thức cú pháp diễn giải nào khác. Văn bản được phân tích cú pháp duy nhất trong phần CDATA là ]]>và nó chấm dứt phần đó.

Do đó, không thể thoát ]]>trong phần CDATA.

EDIT3: Phần tương tự cũng đọc:

2.7 Phần CDATA

[Định nghĩa: Phần CDATA có thể xảy ra ở bất cứ đâu có thể xảy ra dữ liệu ký tự; chúng được sử dụng để thoát khỏi các khối văn bản chứa các ký tự thường được nhận dạng là đánh dấu. Các phần CDATA bắt đầu bằng chuỗi "<! [CDATA [" và kết thúc bằng chuỗi "]]>":]

Sau đó, có thể có một phần CDATA bất cứ nơi nào dữ liệu ký tự có thể xảy ra, bao gồm nhiều phần CDATA liền kề thay cho một phần CDATA. Điều đó cho phép có thể phân tách ]]>mã thông báo và đặt hai phần của nó vào các phần CDATA liền kề.

Ví dụ:

<![CDATA[Certain tokens like ]]> can be difficult and <invalid>]]> 

nên được viết là

<![CDATA[Certain tokens like ]]]]><![CDATA[> can be difficult and <valid>]]> 

1
Thật. Chà, tôi không phải là kiểu học giả nhưng như tôi đã nói trong câu hỏi, tôi chỉ tò mò về điều này. Thành thật mà nói, tôi sẽ chỉ nói về điều này, bởi vì tôi hầu như không thể hiểu được cú pháp được sử dụng cho quy tắc. Cảm ơn câu trả lời của bạn.
Juan Pablo Califano

39
Đây không phải là một câu hỏi học thuật. Hãy suy nghĩ về một nguồn cấp dữ liệu RSS của một bài đăng blog có chứa một cuộc thảo luận về CDATA.
usr

4
Tôi có nghĩa là "học thuật" theo nghĩa: "thú vị để thảo luận, nhưng không sử dụng thực tế". Nói chung, CDATA không hữu ích, nó chỉ là một cách để tuần tự hóa văn bản XML và nó tương đương về mặt ngữ nghĩa với việc thoát các ký tự đặc biệt bằng cách sử dụng các thực thể ký tự & lt; & gt; và & quot;. Các thực thể ký tự là giải pháp đơn giản nhất, mạnh mẽ nhất và tổng quát nhất, vì vậy hãy sử dụng nó thay vì các phần CDATA. Nếu bạn sử dụng một thư viện XML thích hợp (thay vì xây dựng các chuỗi XML), bạn thậm chí không phải nghĩ về nó.
ddaa

5
Tôi vừa bị cắn bởi cái này vì tôi đang cố mã hóa một số Javascript đã nén thành thẻ <script> như: <script>/*<![CDATA[*/javascript goes here/*]]>*/</script>và javascript của tôi chỉ bao gồm chuỗi đó! Tôi thích ý tưởng chia thành nhiều phần CDATA ...
NickZoic

3
Tôi đã trải nghiệm điều này trong thế giới thực. Trong khi đọc kết xuất wikipedia và viết một tệp xml khác, tôi đã gặp điều này trên trang cho Ủy ban An toàn Giao thông Quốc gia . Nó chứa US $> 100 triệu (2013) cho ngân sách trong hộp thông tin. Nguồn xml chứa [[United States dollar|US$]]&gt;100 million (2013)được [[United States dollar|US$]]>100 million (2013)đọc bởi người đọc và người viết đã chọn sử dụng CDATA để thoát văn bản và thất bại.
Paul Jackson

169

Bạn phải chia dữ liệu của bạn thành nhiều phần để che giấu ]]>.

Đây là toàn bộ:

<![CDATA[]]]]><![CDATA[>]]>

Cái đầu tiên <![CDATA[]]]]>]]. Thứ hai <![CDATA[>]]>>.


1
Cảm ơn câu trả lời của bạn. Tôi đã tìm kiếm một cái gì đó giống như một dấu gạch chéo ngược (trong các chuỗi trong C, PHP, Java, v.v.). Theo quy tắc được trích dẫn bởi ddaa, có vẻ như không có chuyện đó.
Juan Pablo Califano

28
Đây phải là câu trả lời được chấp nhận. Chạy trốn là một thuật ngữ hơi mơ hồ, nhưng câu trả lời này chắc chắn đề cập đến tinh thần trốn thoát . Quá tệ, nó không phù hợp với quan niệm hẹp hòi của OP về việc trốn thoát , điều này tùy tiện đòi hỏi phải có ký tự dấu gạch chéo ngược vì một số lý do.
G-Wiz

5
Vì vậy, tóm lại, thoát ]]>như ]]]]><![CDATA[>. 5 lần chiều dài ... wow. Nhưng sau đó, đó là một chuỗi không phổ biến.
Brilliand

5
Không chỉ có độ dài vui nhộn 5x, nó thậm chí không phải là một chuỗi không phổ biến trong mã, đây là trường hợp sử dụng chính của CDATA! Giả sử JavaScript nén sẽ loại bỏ khoảng trắng, bạn có thể truy cập vào một trường theo tên từ một mảng tên theo chỉ mục, chẳng hạn như "if (các trường [tên trường [0]]> 3)" và bây giờ bạn phải thay đổi nó thành "if ( các trường [tên trường [0]]]]> <! [CDATA [> 3) ", đánh bại mục đích sử dụng CDATA để làm cho nó dễ đọc hơn, LOL. Tôi muốn tát bằng miệng bất cứ ai nghĩ ra cú pháp CDATA.
Triynko

1
Thoát, hay chính xác hơn là trích dẫn, có nghĩa là chèn một số văn bản trong ngữ cảnh mà văn bản thô có nghĩa là KHÔNG rời khỏi bối cảnh. Nó không có gì để làm với dấu gạch chéo ngược. Và câu trả lời này không thoát hoặc trích dẫn vì nó tạo ra hai phần CDATA thay vì một phần.
ddaa

17

Bạn không thoát khỏi ]]>nhưng bạn thoát khỏi cái >sau ]]bằng cách chèn vào ]]><![CDATA[trước >, hãy nghĩ về điều này giống như một chuỗi \trong C / Java / PHP / Perl nhưng chỉ cần trước a >và sau a ]].

BTW,

Câu trả lời của S.Lott giống như thế này, chỉ diễn đạt khác nhau.


2
Tôi thích từ ngữ này. :)
Brilliand

3
Cách nói này mang lại cho mọi người ý tưởng sai lầm. Đây không phải là thoát. ]]]]><![CDATA[>không phải là một chuỗi phép thuật cho ]]>. ]]]]>có các ]]ký tự dưới dạng dữ liệu và ]]>kết thúc phần CDATA hiện tại. <![CDATA[>bắt đầu một phần CDATA mới và đặt >vào đó. Chúng thực sự là hai yếu tố khác nhau và sẽ được xử lý khác nhau khi làm việc với trình phân tích cú pháp DOM. Bạn nên nhận thức được điều đó. Cách làm này tương tự như vậy ]]]><![CDATA[]>, ngoại trừ nó đặt ]vào ]>CDATA thứ nhất và thứ hai. Sự khác biệt vẫn còn.
Aidiakapi

Sự khác biệt là quá cường điệu, vì nội dung CDATA được coi là một khoảng văn bản của văn bản thoát. Chỉ khi gây rối với DOM thì nó mới thực sự quan trọng và ở cấp độ đó, bạn mới xử lý các ranh giới vô hình khác như văn bản, nhận xét và xử lý các nút hướng dẫn.
Beejor

7

Câu trả lời của S. Lott là đúng: bạn không mã hóa thẻ kết thúc, bạn chia nó qua nhiều phần CDATA.

Cách xử lý vấn đề này trong thế giới thực: sử dụng trình soạn thảo XML để tạo tài liệu XML sẽ được đưa vào hệ thống quản lý nội dung, hãy thử viết một bài viết về các phần CDATA. Thủ thuật thông thường của bạn về việc nhúng các mẫu mã trong phần CDATA sẽ khiến bạn thất bại ở đây. Bạn có thể tưởng tượng làm thế nào tôi học được điều này.

Nhưng trong hầu hết các trường hợp, bạn sẽ không gặp phải điều này và đây là lý do: nếu bạn muốn lưu trữ (giả sử) văn bản của tài liệu XML làm nội dung của một phần tử XML, có thể bạn sẽ sử dụng phương thức DOM, ví dụ:

XmlElement elm = doc.CreateElement("foo");
elm.InnerText = "<[CDATA[[Is this a problem?]]>";

Và DOM khá hợp lý thoát khỏi <và>, điều đó có nghĩa là bạn đã vô tình nhúng một phần CDATA trong tài liệu của bạn.

Ồ, và điều này thật thú vị:

XmlDocument doc = new XmlDocument();

XmlElement elm = doc.CreateElement("doc");
doc.AppendChild(elm);

string data = "<![[CDATA[This is an embedded CDATA section]]>";
XmlCDataSection cdata = doc.CreateCDataSection(data);
elm.AppendChild(cdata);

Đây có lẽ là một ideosyncrasy của .NET DOM, nhưng điều đó không tạo ra ngoại lệ. Ngoại lệ được ném ở đây:

Console.Write(doc.OuterXml);

Tôi đoán rằng những gì đang diễn ra dưới mui xe là XmlDocument đang sử dụng XmlWriter tạo ra đầu ra của nó và XmlWriter kiểm tra độ chính xác khi viết.


Chà, tôi đã có một ví dụ gần như "thế giới thực". Tôi thường tải Xml từ Flash có chứa đánh dấu html trong các phần CDATA. Có một cách để thoát khỏi nó có thể hữu ích, tôi đoán vậy. Nhưng dù sao, trong trường hợp đó, nội dung CDATA thường là XHTML hợp lệ và do đó CDATA "bên ngoài" có thể tránh được hoàn toàn.
Juan Pablo Califano

2
CDATA gần như luôn luôn có thể tránh được hoàn toàn. Tôi thấy rằng những người đấu tranh với CDATA rất thường xuyên không hiểu những gì họ thực sự cố gắng làm và / hoặc công nghệ họ đang sử dụng thực sự hoạt động như thế nào.
Robert Rossney

Ồ, tôi cũng nên nói thêm rằng lý do duy nhất mà CMS mà tôi đã đề cập trong câu trả lời của tôi đã sử dụng CDATA là tôi đã viết nó và tôi không hiểu những gì tôi thực sự cố gắng làm và / hoặc cách thức công nghệ hoạt động. Tôi không cần sử dụng CDATA.
Robert Rossney

Nếu bạn đang sử dụng .net, có thể tránh nhận xét trước về CDATA - chỉ cần viết nội dung dưới dạng chuỗi và khung sẽ thực hiện tất cả các lối thoát (và không chú ý khi đọc) cho bạn từ thế giới thực .... ... xmlStream.WriteStartEuity ("Chưa xử lýHtml"); xmlStream.WriteString (Chưa xử lýHtml); xmlStream.WriteEndEuity ();
Đánh dấu Mullin


3

Đây là một trường hợp khác ]]>cần phải thoát. Giả sử chúng ta cần lưu một tài liệu HTML hợp lệ hoàn hảo bên trong khối CDATA của tài liệu XML và nguồn HTML xảy ra để có khối CDATA riêng. Ví dụ:

<htmlSource><![CDATA[ 
    ... html ...
    <script type="text/javascript">
        /* <![CDATA[ */
        -- some working javascript --
        /* ]]> */
    </script>
    ... html ...
]]></htmlSource>

hậu tố CDATA nhận xét cần được thay đổi thành:

        /* ]]]]><![CDATA[> *//

vì trình phân tích cú pháp XML sẽ không biết cách xử lý các khối nhận xét javascript


Đây không phải là một trường hợp đặc biệt. Đơn giản chỉ cần thay thế ]]>bằng ]]]]><![CDATA[>vẫn áp dụng ở đây. Thực tế là JavaScript, hoặc nhận xét không quan trọng.
Thomas Grainger


1

Một cách sạch hơn trong PHP:

   function safeCData($string)
   {
      return '<![CDATA[' . str_replace(']]>', ']]]]><![CDATA[>', $string) . ']]>';
   }

Đừng quên sử dụng một chuỗi liên kết an toàn đa bào nếu được yêu cầu (không phải là latin1 $string):

   function mb_str_replace($search, $replace, $subject, &$count = 0)
   {
      if (!is_array($subject))
      {
         $searches = is_array($search) ? array_values($search) : array ($search);
         $replacements = is_array($replace) ? array_values($replace) : array ($replace);
         $replacements = array_pad($replacements, count($searches), '');
         foreach ($searches as $key => $search)
         {
            $parts = mb_split(preg_quote($search), $subject);
            $count += count($parts) - 1;
            $subject = implode($replacements[$key], $parts);
         }
      }
      else
      {
         foreach ($subject as $key => $value)
         {
            $subject[$key] = mb_str_replace($search, $replace, $value, $count);
         }
      }
      return $subject;
   }

Bạn có thể giải thích downvote của bạn? Nói rằng tôi đã làm một sai lầm không hữu ích như giải thích nó ở đâu.
Alain Tiemblo

Không cần phải thay thế an toàn đa bào nếu bạn đang sử dụng UTF-8. Tôi đã không downvote mặc dù :)
frodeborli

-1

Tôi không nghĩ rằng làm gián đoạn CDATA là cách tốt để đi. Đây là sự thay thế của tôi ...

Sử dụng ]cho chuỗi thoát theo sau là giá trị hex của nhân vật của bạn. Thích trong &#xhhhh;=>]<unicode value>;

Bằng cách này, nếu bạn cố gắng ghi lại ]]>mã hóa fn của bạn sẽ tạo ra ]005D;]005D;]003E;cái nào cũng được trong CDATA.

Tốt hơn là thoát theo tên thực thể, bởi vì chúng không được giải mã mỗi lần trong ứng dụng của bạn và bạn có thể có các ưu tiên khác nhau để thoát các thực thể bằng dấu và so với thoát một số ký tự / chuỗi khác. Kết quả là bạn có nhiều quyền kiểm soát hơn đối với nội dung của CDATA.


-2

Xem cấu trúc này:

<![CDATA[
   <![CDATA[
      <div>Hello World</div>
   ]]]]><![CDATA[>
]]>

Đối với (các) thẻ CDATA bên trong, bạn phải đóng ]]]]><![CDATA[>thay vì ]]>. Đơn giản như vậy.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.