Monday, September 7, 2009

8. XML parsing

While Cocoa has a NSXML class for tree based parsing of XML documents; Cocoa Touch, on iPhone, has only the event driven counterpart NSXMLParser. Therefore there are only two possible solution for XML parsing on iPhone: using NSXMLParser or libxml2.

I am going to discuss the first one: NSXMLParser.


Event driven XML parsing using NSXMLParser



To parse a XML document with NSXMLParser we need to:

  1. To get the URL of the XML file.

  2. To initialize a NSXMLParser instance using the URL.

  3. To start the parser.

  4. To implement some of the methods declared by the NSXMLParserDelegateEventAdditions category of the NSObject class.

  5. To set the instance of the class, that implements NSXMLParserDelegateEventAdditions category, as the delegate of the NSXMLParser instance.



Two of the methods, that every program will probably implement, are written in the following code.

- (void) parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict
{

}

- (void) parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
{

}


NSXMLParser is a SAX like parser. Therefore it is not aware of the XML hierarchy. It simply parse all the XML elements as a plain sequence. To get a tree hierarchy, that matches XML document hieararchy, using the NSXMLParser we have to provide our own code.

To create an object hierarchy that matches XML one, first of all, we have to implement a class that supports a tree graph based hierarchy. A tipical example is a node based architecture.

@interface MyNode : NSObject {
@public
NSString * _name;
MyNode * _parent;
NSMutableArray * _children;
}
@end


For this class we should also declare and define some accessor methods (I don't like properties of Objective-C 2.0) like the methods to access the _name field and the _parent field.
Therefore we can declare the needed methods.

@interface MyNode : NSObject {
@public
NSString * _name;
MyNode * _parent;
NSMutableArray * _children;
}
- (NSString *) name;
- (void) setName:(NSString *)name;
- (MyNode *) parent;
- (void) setParent:(MyNode *)parent;
@end


And then define them.

@implementation
#pragma mark ACCESSORS
/* _name */
- (NSString *) name {
return _name;
}

- (void) setName:(NSString *)name {
if (_name != name) {
[_name release];
_name = [name retain];
}
} /* END _name */

/* _parent */
- (MyNode *) parent {
return _parent;
}

- (void) setParent:(MyNode *)parent {
if (_parent != parent) {
[_parent release];
_parent = [parent retain];
}
} /* END _parent */


To implement the accessor methods for _children field is a little different process. But Xcode has some macros to help us. Select the entire line of code that contains the _children field declaration like the following figure.



Then choose the item "Place Accessor Decls on Clipboard" from the "Code" submenu of the "User Scripts" menu.



Then, place the cursor in an empty line, between the end of setParent: method declaration and the @end macro, and paste (COMMAND+V).

Select once more the _children field declaration...



... and choose the item "Place Accessor Defs on Clipboard", this time.
Then go to the implementation source file (MyNode.m), place the cursor in an empty line between the end of the setParent: method definition and the @end macro, and paste (COMMAND+V).

Xcode will create all the KVC compliant accessor methods of the _children instance variable for you.

So, the class MyNode is essentially implemented.

No comments: