NSXMLParserはHTMLのような、アバウトな記述のMLを解析させると確実にエラーを吐いて、途中で解析を投げ出してしまう。
エラー無視のパラメータとかはどうやらないらしい。
というわけで、必然的にCライブラリのlibxml2に白羽の矢がたった。
コレをSAXインタフェースで使用する。
その方法は、google先生にお伺いを立てると、いろいろ教えていただけるので、記載はしない。
だが、SAXインタフェース上で、
cdataBlock検出時(たぶんその他のcharacters/commentでも同様)に渡される文字列にヒトクセあったので備忘録として記載する。
static void cdataHandler(void * ctx, const xmlChar * value, int len)
{
}
コレが、cdataBlock検出時に呼び出される関数。
こいつの第二引数valueにUTF-8の文字列が格納されている。
そして、第三引数lenには、valueの有効文字列長が格納されている。
クセその1
scriptなどの大規模な文字列の場合、文字列が一括で渡されない。
具体的には、1000Byte単位で上記関数が呼び出される。
1000Byteを超える場合は、複数回関数が呼び出される。
当然、受け側としては受け取った文字列を連結して保持する事になる。
そこで、もう一つのクセが待ち構えているのだ。
クセその2
この文字列の終端はnullターミネートされていない(!)
このため、通常の手段としてNSMutableStringに蓄積しようとしても、NSMutableDataに溜め込もうとしても、strcatで連結しようとしてもゴミが残留するのだ。
このゴミが、最終的にNSStringに変換しようとするときに高確立でエラーとなって、NSStringが生成されないという現象が発生する。
ではどうするか?
ここで信用できる情報は何か?
文字列データは、nullターミネートされていないが、それ以外の部分はおおむね正しい。
そして、文字列長情報も問題ない。
信用できないのは、文字列長を超えた部分に存在しているゴミデータである。
ならば。
文字列の連結の度、有効文字列長の末尾に、自力でnullターミネートを書き込んでやればよろしい。
具体的には、
charLen_ = charLen_ + len;
if ( chars_ == nil ) {
chars_ = xmlStrdup(value);
} else {
chars_ = xmlStrcat(chars_, value);
}
chars_[charLen_] = 0x00;
xmlStrxxx関数はxmlChar型の操作関数だ。
これでNSStringに変換出来ないという症状は押さえ込めた。
ちなみに、1000byte未満の文字列の場合、正しくnullターミネートされているらしく、
NSString変換に失敗する事は無いようである。