web-dev-qa-db-ja.com

XMLでCDATA終了トークンをエスケープする方法はありますか?

CDATAエンドトークン(]]>)xmlドキュメントのCDATAセクション内。または、より一般的には、CDATA内で使用するためのエスケープシーケンスがある場合(ただし、存在する場合は、とにかく開始トークンまたは終了トークンをエスケープすることだけが意味があると思います)。

基本的に、CDATAに開始トークンまたは終了トークンを埋め込み、それを解釈するのではなく、単なる別の文字シーケンスとして扱うようにパーサーに指示できますか。

おそらく、XML構造またはコードをリファクタリングする必要がある場合は、リファクタリングする必要がありますが、過去3年間ほど毎日xmlを使用してきましたが、この問題は一度もありませんでしたが、それが可能かどうか疑問に思っていました。単なる好奇心から。

編集:

HTMLエンコードを使用する以外...

125

明らかに、この質問は純粋に学術的なものです。幸いなことに、非常に明確な答えがあります。

CDATA終了シーケンスをエスケープすることはできません。 XMLのプロダクションルール20 仕様 は非常に明確です。

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

編集:この製品ルールは、文字通り「CDataセクションには必要なものを含めることができますが、シーケンス ']]>'。例外なし」を意味します。

EDIT2: 同じセクション も読み取ります:

CDATAセクション内では、CDEnd文字列のみがマークアップとして認識されるため、左山括弧とアンパサンドがリテラル形式で発生する場合があります。 「<」と「&」を使用してエスケープする必要はありません(また、エスケープすることはできません)。 CDATAセクションはネストできません。

つまり、エンティティ参照、マークアップ、またはその他の形式の解釈された構文を使用することはできません。 CDATAセクション内の唯一の解析されたテキストは]]>であり、セクションを終了します。

したがって、CDATAセクション内で]]>をエスケープすることはできません。

EDIT3: 同じセクション も読み取ります:

2.7 CDATAセクション

[定義:CDATAセクションは、文字データが発生する場所であればどこでも発生する可能性があります。これらは、そうでなければマークアップとして認識される文字を含むテキストのブロックをエスケープするために使用されます。 CDATAセクションは、文字列「<![CDATA [」で始まり、文字列「]]>」で終わります:]

次に、単一のCDATAセクションの代わりに複数の隣接するCDATAセクションを含む、文字データが発生する可能性のある場所にCDATAセクションが存在する場合があります。これにより、]]>トークンを分割し、その2つの部分を隣接するCDATAセクションに配置できるようになります。

例:

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

として書かれるべきである

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

]]>を隠すには、データを分割する必要があります。

すべてがここにあります:

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

最初の<![CDATA[]]]]>には]]があります。 2番目の<![CDATA[>]]>には>があります。

167
S.Lott

]]>をエスケープしませんが、>の前に]]を挿入することにより、]]><![CDATA[の後に>をエスケープします。これは、C/Java/PHP/Perl文字列の\と同様ですが、>の前と]]の後にのみ必要です。

ところで、

S.Lottの答えはこれと同じで、言い方が異なります。

15
Jason Pyeron

S. Lottの答えは正しいです。終了タグをエンコードせず、複数のCDATAセクションに分割します。

現実の世界でこの問題に対処する方法:XMLエディターを使用して、コンテンツ管理システムにフィードされるXML文書を作成し、CDATAセクションに関する記事を書いてみてください。 CDATAセクションにコードサンプルを埋め込むという通常のトリックは、ここでは失敗します。私がこれをどのように学んだか想像できます。

しかし、ほとんどの状況では、これに遭遇することはありません。その理由は次のとおりです。XMLドキュメントのテキストをXML要素のコンテンツとして(たとえば)格納する場合は、おそらくDOMメソッドを使用します。

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

そして、DOMは<と>をかなり合理的にエスケープします。これは、ドキュメントにCDATAセクションを誤って埋め込まなかったことを意味します。

ああ、これは面白いです:

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);

これはおそらく.NET DOMの理念同期ですが、例外はスローされません。ここで例外がスローされます:

Console.Write(doc.OuterXml);

内部で起こっていることは、XmlDocumentがXmlWriterを使用して出力を生成し、XmlWriterが書き込み時に整形式かどうかをチェックしていることだと思います。

7
Robert Rossney

単に]]> with ]]]]><![CDATA[>

5
Thomas Grainger

]]>はエスケープする必要があります。完全に有効なHTMLドキュメントをXMLドキュメントのCDATAブロック内に保存する必要があり、HTMLソースにたまたまそれ自身のCDATAブロックがあるとします。例えば:

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

コメント付きのCDATAサフィックスを次のように変更する必要があります。

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

xMLパーサーはjavascriptコメントブロックの処理方法を知らないため

3
Shawn Becker

PHPの場合:'<![CDATA['.implode(explode(']]>', $string), ']]]]><![CDATA[>').']]>'

1
user2194495

PHPのクリーンな方法:

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

必要に応じてマルチバイトセーフstr_replaceを使用することを忘れないでください(非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;
   }
1
Alain Tiemblo

別の解決策は、]]> 沿って ]]]><![CDATA[]>

0
mik

この構造を参照してください。

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

内部CDATAタグの場合、]]]]><![CDATA[>の代わりに]]>で閉じる必要があります。そのような単純な。

0
Chad Kuehn