XML Parsing Basics

The essential steps for parsing an XML document using NSXMLParser are straightforward. It requires you complete the following general steps:

  1. Locate the XML.

    Listing 1 shows code that lets the user select an XML file from a file-system browser (NSOpenPanel).



    Listing 1  Opening an XML file

    - (void)openXMLFile {
     
        NSArray *fileTypes = [NSArray arrayWithObject:@"xml"];
        NSOpenPanel *oPanel = [NSOpenPanel openPanel];
        NSString *startingDir = [[NSUserDefaults standardUserDefaults] objectForKey:@"StartingDirectory"];
        if (!startingDir)
            startingDir = NSHomeDirectory();
        [oPanel setAllowsMultipleSelection:NO];
        [oPanel beginSheetForDirectory:startingDir file:nil types:fileTypes
          modalForWindow:[self window] modalDelegate:self
          didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:)
          contextInfo:nil];
    }
     
    - (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo {
        NSString *pathToFile = nil;
        if (returnCode == NSOKButton) {
            pathToFile = [[[sheet filenames] objectAtIndex:0] copy];
        }
        if (pathToFile) {
            NSString *startingDir = [pathToFile stringByDeletingLastPathComponent];
            [[NSUserDefaults standardUserDefaults] setObject:startingDir forKey:@"StartingDirectory"];
            [self parseXMLFile:pathToFile];
        }
    }

    Although an XML file is the common case, the source of the XML might not be a file. You could receive the XML from another object as a property-list object (such as an NSDictionary) or as a stream of bytes over a network. In cases like these, you must convert the form of the XML to an NSData object before initializing the NSXMLParser instance (see following step)

  2. Create and initialize an instance of NSXMLParser., ensuring that you set a delegate.

    Listing 2 illustrates how you might do this.



    Listing 2  Creating and initializing a NSXMLParser instance

    - (void)parseXMLFile:(NSString *)pathToFile {
        BOOL success;
        NSURL *xmlURL = [NSURL fileURLWithPath:pathToFile];
        if (addressParser) // addressParser is an NSXMLParser instance variable
            [addressParser release];
        addressParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];
        [addressParser setDelegate:self];
        [addressParser setShouldResolveExternalEntities:YES];
        success = [addressParser parse]; // return value not used
                    // if not successful, delegate is informed of error
    }

    In this method, the client object converts the path to the XML file to an NSURL object and then uses that object to initialize the NSXMLParser instance with initWithContentsOfURL:. It also sets the delegate to be itself and lets the parser know it wants to resolve external entities (such as external DTD declarations). Other NSXMLParser methods let you set various namespace-related options. Finally, the client sends parse to the NSXMLParser instance to have it begin parsing the XML.

    If the XML was in some form other than a file, you would convert it to an NSData object and then use the initWithData: initializer:

    addressParser = [[NSXMLParser alloc] initWithData:xmlData];
  3. Implement the delegation methods that are of interest to you.

    When the NSXMLParser object parses the XML, it sends a message to its delegate for each XML construct it encounters (but only if the delegate implements the associated method). Implementations of these methods vary by type of construct: DTD declarations, namespace prefixes, elements, and so on. Elements are the most common type of XML construct processed; see “Handling XML Elements and Attributes” for details.

    All parsing operations begin with the delegate receiving parserDidStartDocument: and end with the delegate receiving parserDidEndDocument: (assuming, of course, the delegate implements the methods). The former method offers an opportunity for allocating and setting up resources needed for the parsing operation; the latter method is a good place to release those resources and properly dispose of any result.

  4. Handle any parsing errors.

    If the parser encounters an error, it stops parsing and invokes the delegation method parser:parseErrorOccurred:. Implement this method to interpret the error and inform the user. (All parser errors are nonrecoverable.) See “Handling Parsing Errors” for further information.

Memory management becomes a heightened concern when you are parsing XML. Processing the XML often requires you to create many objects; you should not allow these objects to accumulate in memory past their span of usefulness. One technique for dealing with these generated objects is for the delegate to create a local autorelease pools at the beginning of each implemented delegation method and release the autorelease pool just before returning. NSXMLParser manages the memory for each object it creates and sends to the delegate.