- Core Foundation-
|
| ノードタイプ | 文字列表現 | 属性情報 |
| kCFXMLNodeTypeDocument | (未使用) | CFXMLDocumentInfo * |
| kCFXMLNodeTypeElement | タグ名 | CFXMLElementInfo* |
| kCFXMLNodeTypeAttribute | (未使用) | (未使用) |
| kCFXMLNodeTypeProcessInstruction | ターゲット名 | CFXMLProcessingInstructionInfo* |
| kCFXMLNodeTypeComment | コメント | NULL |
| kCFXMLNodeTypeText | テキスト | NULL |
| kCFXMLNodeTypeCDATASection | CDATA | NULL |
| kCFXMLNodeTypeDocumentFragment | (未使用) | (未使用) |
| kCFXMLNodeTypeEntity | エンティティ名 | CFXMLEntityInfo* |
| kCFXMLNodeTypeEntityReference | エンティティリファレンス | CFXMLEntityReferenceInfo* |
| kCFXMLNodeTypeDocumentType | トップレベルのエレメント名 | CFXMLDocumentTypeInfo* |
| kCFXMLNodeTypeWhitespace | 空白文字 | NULL |
| kCFXMLNodeTypeNotation | ノテーション名 | CFXMLNotationInfo* |
| kCFXMLNodeTypeElementTypeDeclaration | タグ名 | CFXMLElementTypeDeclarationInfo* |
| kCFXMLNodeTypeAttributeListDeclaration | タグ名 | CFXMLAttributeListDeclarationInfo* |
たとえば、ドキュメントノードは、dataTypeCode に kCFXMLNodeTypeDocument が返ってくることで判別できる。文字列表現はなしで、属性として CFXMLDocumentInfo 型が取れる。こいつは、ソースの URL とエンコーディングを持っているんだ。
と、いう感じでノードを取り扱うことができる。じゃ、サンプル。
- (id)outlineView:(NSOutlineView*)outlineView
objectValueForTableColumn:(NSTableColumn*)tableColumn
byItem:(id)item
{
CFXMLNodeRef node = CFXMLTreeGetNode((CFXMLTreeRef)item);
...
switch (CFXMLNodeGetTypeCode(node)) {
...
case kCFXMLNodeTypeElement: {
const CFXMLElementInfo* element =
(CFXMLElementInfo*)CFXMLNodeGetInfoPtr(node);
NSArray* attributeOrder =
(NSArray*)element->attributeOrder;
NSDictionary* attributes =
(NSDictionary*)element->attributes;
string = [NSMutableString string];
[string appendFormat:@"<%@", CFXMLNodeGetString(node)];
for (i = 0; i < [attributeOrder count]; i++) {
id attr = [attributeOrder objectAtIndex:i];
[string appendFormat:@" %@="%@"",
attr, [attributes objectForKey:attr]];
}
[string appendFormat:@">"];
return string;
}
...
}
return (id)CFXMLNodeGetString(node);
}
サンプルでは、まず CFXMLNode を取り出して、CFXMLNodeGetTypeCode() を使ってノードタイプを判別している。それに対して swtich をかけるんだ。それがエレメントノードだったら、CFXMLNodeGetInfoPtr() を使って属性を取り出す。そうすると、タグの属性が入った NSDictionary にアクセスできる、ってわけだ。
とまぁ、こんな感じでノードを使おう。
■サンプルダウンロード:
XMLTreeViewer.tar.gz
CFXMLTreeCreateFromData() は、XML データを指定して、パースされた木構造を受け取るための API。成功したときは問題ないんだけど、失敗したときは NULL が返ってくるだけなんだ。いったいどこで XML パースが失敗したのか分からないんだ。これは悲しい。失敗した原因と行数ぐらいは知りたいよな。
エラーの原因をつかむには、CFXMLParser を使う必要がある。じつは、CFXMLTreeCreateFromData() の中で、CFXMLParser を作成してパースしているんだ。だから、パースが終わった後、その CFXMLParser を返してやる API に変更してやればいい。
というわけで、作ってみた。CFXMLTreeCreateFromDataWithParser() っていう API だ。
CFXMLTreeRef CFXMLTreeCreateFromDataWithParser(
CFAllocatorRef allocator,
CFDataRef xmlData,
CFURLRef dataSource,
UInt32 parseOptions,
CFIndex parserVersion,
CFXMLParserRef* parserOut);
CFXMLTreeCreateFromData() API の後ろに CFXMLParserRef へのポインタをくっつけた。パースが終わったら、ここにパーサを設定するんだ。
で、これが実装の一部。Darwing で公開されている CFXMLParser.c を参考にした。
CFXMLTreeRef CFXMLTreeCreateFromDataWithParser(
CFAllocatorRef allocator,
CFDataRef xmlData,
CFURLRef dataSource,
UInt32 parseOptions,
CFIndex parserVersion,
CFXMLParserRef* parserOut)
{
CFXMLParserRef parser;
CFXMLParserCallBacks callbacks;
CFXMLTreeRef result;
...
// Create parser
parser = CFXMLParserCreate(
allocator,
xmlData,
dataSource,
parseOptions,
parserVersion,
&callbacks,
NULL);
// Parse
if (CFXMLParserParse(parser)) {
result = (CFXMLTreeRef)CFXMLParserGetDocument(parser);
}
else {
...
}
if (parserOut) {
// Set parser output
*parserOut = parser;
}
else {
// Release parser
CFRelease(parser);
}
return result;
}
CFXMLParserCreate() でパーサを作って、CFXMLParserParse() でパースをしてやる。そのパーサを、引数 parserOut に設定しているんだ。これで、呼び出し側でパーサを使えるぜ。ただし、そのパーサを release しておく必要があるんで注意。
この API の使い方は、こんな感じになる。
- (BOOL)loadDataRepresentation:(NSData*)data
ofType:(NSString*)type
{
CFXMLParserRef parser;
CFXMLNodeRef document;
const CFXMLDocumentInfo* docInfo;
NSStringEncoding encoding;
if ([type isEqualToString:@"XMLDocumentType"]) {
// Parse the XML and get the CFXMLTree
_xmlTree = CFXMLTreeCreateFromDataWithParser(
kCFAllocatorDefault,
(CFDataRef)data,
NULL,
kCFXMLParserSkipWhitespace,
kCFXMLNodeCurrentVersion,
&parser);
if (!_xmlTree && parser) {
// Parse error
NSString* message;
message = [NSString stringWithFormat:@"Line %d: %@",
CFXMLParserGetLineNumber(parser),
(NSString*)CFXMLParserCopyErrorDescription(parser)];
_errMessage = [message retain];
CFRelease(parser);
return YES;
}
CFRelease(parser);
...
}
return NO;
}
CFXMLTreeCreateFromDataWithParser() を使って XML ドキュメントをパースする。返り値が NULL だったらパースは失敗していて、パーサから CFXMLParserGetLineNumber() と CFXMLParserCopyErrorDescription を使うことで、エラーの原因を取り出すことができるんだ。これで XML パースが使いやすくなるよ。
■サンプルダウンロード:
XMLTreeViewer.tar.gz