XMLでCDATA終了トークンをエスケープする方法はありますか?
質問
XMLドキュメントのCDATAセクション内でCDATA終了トークン(]]>
)をエスケープする方法があるかどうか疑問に思っていました。または、より一般的には、CDATA内で使用するためのエスケープシーケンスがある場合(ただし、存在する場合は、とにかく開始トークンまたは終了トークンのみをエスケープするのが理にかなっていると思います)。
基本的に、CDATAに開始トークンまたは終了トークンを埋め込んで、それを解釈せず、単なる別の文字シーケンスとして扱うようにパーサーに指示できますか。
おそらく、XML構造またはコードをリファクタリングする必要がある場合は、リファクタリングする必要がありますが、過去3年間ほど毎日xmlを使用してきましたが、この問題は、可能かどうか疑問に思っていました。好奇心から。
編集:
htmlエンコードの使用以外...
解決
明らかに、この質問は純粋に学術的なものです。幸いなことに、非常に明確な答えがあります。
CDATA終了シーケンスをエスケープすることはできません。 XML 仕様のプロダクションルール20は非常に明確です:
[20] CData ::= (Char* - (Char* ']]>' Char*))
EDIT:この製品ルールは、文字通り<!> quot;を意味しますが、CDataセクションには必要なものを含めることができますが、シーケンス ']] <!> gt;'を使用できます。例外なし。<!> quot;。
EDIT2:同じセクションも読み取ります:
CDATAセクション内では、CDEnd文字列のみがマークアップとして認識されるため、左山括弧とアンパサンドがリテラル形式で発生する可能性があります。 <!> quot;
<
<!> quot;を使用してエスケープする必要はありません(できません)。および<!> quot;&
<!> quot;。 CDATAセクションはネストできません。
つまり、エンティティ参照、マークアップ、またはその他の解釈された構文を使用することはできません。 CDATAセクション内の解析されたテキストは]]>
のみで、セクションを終了します。
したがって、CDATAセクション内で<=>をエスケープすることはできません。
EDIT3:同じセクションも読み取ります:
2.7 CDATAセクション
[定義:CDATAセクションは、文字データが発生する場所であればどこでも発生する可能性があります。これらは、そうでなければマークアップとして認識される文字を含むテキストのブロックをエスケープするために使用されます。 CDATAセクションは、文字列<!> quot; <!> lt;![CDATA [<!> quot;文字列<!> quot;]] <!> gt; <!> quot ;:]
で終わる
次に、単一のCDATAセクションの代わりに隣接する複数のCDATAセクションを含む、文字データが発生する可能性のある場所にCDATAセクションがある場合があります。これにより、<=>トークンを分割し、その2つの部分を隣接するCDATAセクションに配置できるようになります。
ex:
<![CDATA[Certain tokens like ]]> can be difficult and <invalid>]]>
と書く必要があります
<![CDATA[Certain tokens like ]]]]><![CDATA[> can be difficult and <valid>]]>
他のヒント
]]>
を隠すには、データを分割する必要があります。
これがすべてです:
<![CDATA[]]]]><![CDATA[>]]>
最初の<![CDATA[]]]]>
には]]
があります。 2番目の<![CDATA[>]]>
には>
があります。
]]>
をエスケープしませんが、>
の前に]]
を挿入して]]><![CDATA[
の後に\
をエスケープします。これはC / Java / PHP / Perlの<=>のように考えてください文字列ですが、<=>の前と<=>の後にのみ必要です。
ところで、
S.Lottの答えはこれと同じで、言い方が異なります。
S。 Lottの答えは正しい:終了タグをエンコードせず、複数のCDATAセクションに分割します。
現実の世界でこの問題に直面する方法:XMLエディターを使用して、コンテンツ管理システムにフィードされるXML文書を作成し、CDATAセクションに関する記事を書きます。 CDATAセクションにコードサンプルを埋め込むという通常のトリックは、ここで失敗します。私がこれをどのように学んだか想像できます。
しかし、ほとんどの状況ではこれに遭遇することはありません。その理由は次のとおりです。XMLドキュメントのテキストをXML要素のコンテンツとして(たとえば)格納する場合は、おそらくDOMメソッドを使用します。例:
XmlElement elm = doc.CreateElement("foo");
elm.InnerText = "<[CDATA[[Is this a problem?]]>";
そして、DOMは<!> ltをかなり合理的にエスケープします。 <!> gt;は、ドキュメントに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が書き込み時に整形式かどうかをチェックしていることだと思います。
単に]]>
を]]]]><![CDATA[>
]]>
をエスケープする必要がある別のケースを次に示します。完全に有効なHTMLドキュメントをXMLドキュメントのCDATAブロック内に保存する必要があり、HTMLソースがたまたまそれ自身のCDATAブロックを持っているとします。例:
<htmlSource><![CDATA[
... html ...
<script type="text/javascript">
/* <![CDATA[ */
-- some working javascript --
/* ]]> */
</script>
... html ...
]]></htmlSource>
コメント化されたCDATAサフィックスを次のように変更する必要があります
/* ]]]]><![CDATA[> *//
XMLパーサーはjavascriptコメントブロックの処理方法を知らないため
PHPの場合:'<![CDATA['.implode(explode(']]>', $string), ']]]]><![CDATA[>').']]>'
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;
}
別の解決策は、]]>
を]]]><![CDATA[]>
に置き換えることです。
次の構造を参照してください:
<![CDATA[
<![CDATA[
<div>Hello World</div>
]]]]><![CDATA[>
]]>
内部CDATAタグの場合、]]]]><![CDATA[>
の代わりに]]>
で閉じる必要があります。簡単です。