NSXMLParser is a great parser, but what if you are reading XML from, say a socket. You may not have the complete message right away, and there may be multiple root tags (or you can close and open a socket and live with the setup time)
Wouldn’t it be cool if we could feed it bits and pieces of XML and it would call us back when a complete message has been parsed? I tackled a problem like this for the ivef-sdk (google it) and here’s what works:
- (bool) parseXMLString:(NSString *)data andBuffer: (bool) cont { // create a seperate memory pool, so we can quickly deallocate temp objects generated by the parser NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // data may or may not be lead by xml definitions, but we need them before each message data = [data stringByReplacingOccurrencesOfString:@"\n" withString:@""]; data = [data stringByReplacingOccurrencesOfString:@"\n" withString:@""]; [m_dataBuffer appendString: data]; NSRange firstTagRange; do { // search if a message is in the buffer firstTagRange = NSMakeRange(NSNotFound , 0); for (NSString *tag in m_closeTags) { NSRange rangeOfTag = [m_dataBuffer rangeOfString:tag]; if (rangeOfTag.location != NSNotFound) { if (firstTagRange.location != NSNotFound) { if (rangeOfTag.location < firstTagRange.location) { firstTagRange = rangeOfTag; } } else { firstTagRange = rangeOfTag; } } } // process a message if found if (firstTagRange.location != NSNotFound) { NSRange firstMessageRange = NSMakeRange(0, firstTagRange.location + firstTagRange.length); NSString *messages = [NSString stringWithFormat:@"\n\n%@\n", [m_dataBuffer substringWithRange:firstMessageRange]]; [m_dataBuffer deleteCharactersInRange:firstMessageRange]; NSXMLParser *tempParser = [[NSXMLParser alloc] initWithData:[messages dataUsingEncoding:NSUnicodeStringEncoding]]; [tempParser setDelegate:self]; [tempParser parse]; [tempParser release]; } } while (firstTagRange.location != NSNotFound); if (!cont) { [m_dataBuffer setString: @""]; } // free temp memory [pool release]; return true; }
Where m_closeTags is an NSArray with …. (you can guess) You use this function by calling it every time you have some data, as you can see it will look for a close tag and then parse the messages until it has no complete message left to parse, the left over bits are saved for your next call in the NSMutableString m_dataBuffer
Yes, indentation is crap, view the source at:
http://code.google.com/p/ivef-sdk/source/browse/ivef-lib/branches/IVEF_0_1_RELEASE/ivef-objc/Classes/ILParser.m