ヨーキョクデイ

いろいろ雑食

ContentHandler::characters() は何度も呼ばれるかもしれない

SAX で XML をパースするときの話。
Xerces-C++: ContentHandler Class Reference より引用。


The Parser will call this method to report each chunk of character data. SAX parsers may return all contiguous character data in a single chunk, or they may split it into several chunks



1 つの要素に対して ContentHandler::characters() が 1 回しか呼ばれないことを前提にしているとひどいことになりかねないので注意。
特に次のような XML 文書だと、Xerces-C++ 2.8.0 で試した結果では CDATA セクションや生のテキストの区切りごとに分割された。実装によっては長いテキストが途中で分断されたりもするらしい。SAX では characters() はいつ呼ばれてもおかしくないため、要素の中身を 1 つのかたまりの文字列としてゲットしたい場合は、startElement() でバッファを作り、characters() が呼ばれるごとにそこに追記し、endElement() で文字列を確定しバッファを破棄するというのが定石らしい。

<?xml version="1.0" encoding="utf-8"?>
<hoge>
 <foo><![CDATA[ab]]><![CDATA[cd]]></foo>
   <!-- "abcd" が欲しい -->
 <bar><![CDATA[12]]>34<![CDATA[56]]>78</bar>
   <!-- "12345678" が欲しい -->
</hoge>

o2on で CDATA セクションをうまい具合に使ってやろうと画策していたところ、この辺の話が重要と思われた次第。