iPhone上XML解析的最佳方法

5
我已熟悉来自iPhone SDK的NSXMLParser,但我觉得它的事件驱动方式对我的目的有些笨拙。我只想提取一些元素值,但是必须处理startElement、foundCharacters和endElement的概念似乎比实际应该做的工作还要多。我是在错误的方向上思考吗?或者在iPhone SDK中,是否有更简单的基于树/ DOM的方法用于处理XML?
如果建议是使用NSXMLParser,则是否有某些设计模式可用于避免我的代码在startElement方法中有5层嵌套的ifs?
3个回答

10
如果你在使用iPhone上进行基于树形结构的解析,可能会导致占用大量内存。相信我,我也曾经历过这种情况,并在开发主要的iPhone应用程序的过程中尝试了许多不同的方法。基于树形结构的解析在下载包含400个非常长的评论(大约600KB原始数据)的流时工作良好。除了结果XML树的大小之外,创建该树时分配的内部内存可能是巨大的。
最终,我创建了NSXMLParser的变体,它从提供的NSInputStream中提取数据,而不是使用一个单独的数据块,并且将每次只传递1KB的数据到libxml进行处理(NSXMLParser也使用libxml,但一次性传递100%的数据)。
源代码可在github上找到(在StreamingXMLParser文件夹中查看)。您还可以在那里找到委托超类;对于大多数解析需求,您可以子类化AQXMLParserDelegate并实现-start[Element]WithAttributes:(NSDictionary *)attrs 和-end [Element]在您的子类中。当发现开始和结束标签时,这些方法将为您调用,并且在结束标签内部,您可以使用self.characters来访问元素的内容字符或CDATA。
有关不同解析器的相对内存占用量的更多信息(尽管是在Mac上而不是iPhone上),请参见我的原始博客文章here和关于NSXMLDocument的后续文章here

谢谢,这是有用的信息。我最终采用了startElement,foundCharacters,endElement模式,还不错,但是现在我注意到NSXMLParser initWithContentsOfURL似乎下载整个文档并将其留在内存中,而不是像您指出的那样流式传输。这有点令人惊讶,因为当您使用基于事件的解析方法时,您不需要访问整个文档。我会研究一下StreamingXMLParser。 - Marplesoft
好的,需要更多的调查。现在我注意到内存占用主要是由于URL下载而不是实际解析。我正在进行异步下载,但似乎没有释放已接收的数据块。 - Marplesoft
是的,NSURLConnection在执行任务时会分配相当多的内存,如果你使用SSL,加密管道会额外分配约1MB的内存。我最终编写了自己的CFHTTPMessageRef包装器,并使用它来获取流以供解析器使用;这个包装器在同一个Github存储库中,位于HTTPMessage子文件夹中。 - Jim Dovey

1
考虑以下代码片段,它使用 libxml2Matt Gallagher 的 libxml2 封装Ben Copsey 的 ASIHTTPRequest 来解析 XML 文档。
类型为 NSArray*nodes 实例将包含 NSDictionary* 对象,您可以递归解析这些对象以获取所需的数据。
或者,如果您知道 XML 文档的结构,您可以编写一个 XPath 查询,直接获取 nodeContentnodeAttribute 值。
ASIHTTPRequest *request = [ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:@"http://stackoverflow.com/"];
[request start];
NSError *error = [request error];
if (!error) {
    NSData *response = [request responseData];
    NSLog(@"Root node: %@", [[self query:@"//" withResponse:response] description]);
}
else 
    @throw [NSException exceptionWithName:@"kHTTPRequestFailed" reason:@"Request failed!" userInfo:nil];
[request release];

...

- (id) query:(NSString *)xpathQuery withResponse:(NSData *)respData {
    NSArray *nodes = PerformXMLXPathQuery(respData, xpathQuery);
    if (nodes != nil)
        return nodes;
    return nil;
}

0

从Seismic XML中重新利用代码可以提供一个非常好的API,它可以从XML创建NSObject子类。

如果建议只使用NSXMLParser,那么有没有某些设计模式可以使用,以避免我的代码在startElement方法中具有5个嵌套if级别?

这取决于你想做什么。你可以将元素名称放入字典中,并根据字典中相关对象采取行动-这实际上就是SeismicXML所做的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接