1. Computing

How to Parse XML Files in Xcode

Parsing XML Files in Objective-C

By

One simple task that is the backbone to many apps is the ability to parse XML files.     And, fortunately, Xcode makes it relatively easy to parse an XML file in Objective-C.

An XML file can contain anything from basic data about your app to an RSS feed for a website.     They can also be a great way of updating information within your app remotely, thus bypassing the need to submit a new binary to Apple simply to add a new item to a list.

So how do we process XML files in Xcode?

The process contains steps for initializing the variables to be used, starting the XML parser process, feeding that process a file, the start of an individual element, the characters (value) within the element, the end of an individual element, and the end of the parsing process.

In this example, we'll be parsing a file from the Internet by passing it a particular web address (URL).

We'll start with building out the header file.     This is an example of a very basic header file for a Detail View Controller with the minimum requirements for parsing our file:
 
 
 
 
 
 

@interface RootViewController : UITableViewController {
      DetailViewController *detailViewController;

      NSXMLParser *rssParser;
      NSMutableArray *articles;
      NSMutableDictionary *item;
      NSString *currentElement;
      NSMutableString *ElementValue;
      BOOL errorParsing;


}

@property (nonatomic, retain) IBOutlet DetailViewController *detailViewController;

- (void)parseXMLFileAtURL:(NSString *)URL;


The parseXMLFileAtURL function will start the process for us.     When it finishes, the NSMutableArray "articles" will hold our data.     The array will be made up of mutable dictionaries with keys related to the field names in the XML file.

Now that we've set up the variables needed, we'll move on to the meet of the process in the .m file:


- (void)parserDidStartDocument:(NSXMLParser *)parser{
      NSLog(@"File found and parsing started");

}


This function runs at the start of the process.     There is no need to put anything in this function, but if you want to perform a task when the file starts to be parsed, this is where you would put your code.


- (void)parseXMLFileAtURL:(NSString *)URL
{

      NSString *agentString = @"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-us) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1";
      NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:
                                                                                      [NSURL URLWithString:URL]];
      [request setValue:agentString forHTTPHeaderField:@"User-Agent"];
      xmlFile = [ NSURLConnection sendSynchronousRequest:request returningResponse: nil error: nil ];


      articles = [[NSMutableArray alloc] init];
      errorParsing=NO;

      rssParser = [[NSXMLParser alloc] initWithData:xmlFile];
      [rssParser setDelegate:self];

      // You may need to turn some of these on depending on the type of XML file you are parsing
      [rssParser setShouldProcessNamespaces:NO];
      [rssParser setShouldReportNamespacePrefixes:NO];
      [rssParser setShouldResolveExternalEntities:NO];

      [rssParser parse];


}


This function instructs the engine to download a file at a particular web address (URL) and start the process for parsing it.     We're telling the remote server that we are a Safari running on Mac just in case the server tries to redirect the iPhone/iPad to a mobile version.     

The options at the end are specific to certain XML files.     Most RSS files and generic XML files won't need them turned on.


- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {

      NSString *errorString = [NSString stringWithFormat:@"Error code %i", [parseError code]];
      NSLog(@"Error parsing XML: %@", errorString);


      errorParsing=YES;
}


This is a simple error checking routing that will set a binary value if it encounters an error.     You may need something more specific here depending on what you are doing. If you simply need to run some code after processing in the case of error, the errorParsing binary variable can be called at that time.


- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
      currentElement = [elementName copy];
      ElementValue = [[NSMutableString alloc] init];
      if ([elementName isEqualToString:@"item"]) {
                item = [[NSMutableDictionary alloc] init];

      }

}


The meat of the XML parser contains three functions, one that runs at the beginning of an individual element, one that runs during the middle of parsing the element, and one that runs at the end of the element.

For this example, we'll be parsing a file similar to RSS files that break down elements into groups under the heading of "items" within the XML file.      At the start of the processing, we are checking for the element name "item" and allocating our item dictionary when a new group is detected. Otherwise, we initialize our variable for the value.




- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
      [ElementValue appendString:string];
}


This is the easy part.     When we find characters, we simply add them to our variable "ElementValue".


- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
      if ([elementName isEqualToString:@"item"]) {
                [articles addObject:[item copy]];
      } else {
                [item setObject:ElementValue forKey:elementName];
      }

}


When we've finished processing an element, we need to do one of two things: (1) if the end element is "item", we've finished our group, so we'll add our dictionary to our array of "articles". Or (2) if the     element is not "item", we'll set the value in our dictionary with a key that matches the element's name.     (This means we don't need an individual variable for each field within the XML file.     We can process them a little more dynamically.)


- (void)parserDidEndDocument:(NSXMLParser *)parser {

if (errorParsing == NO)
      {
          NSLog(@"XML processing done!");
      } else {
     NSLog(@"Error occurred during XML processing");
}

}


This is the last function needed for our parsing routine.     It simply ends the document.     You'll put any code you want to finish up the process here, or anything special you might want to do in case of error.

One thing many apps might want to do here is save the data and/or XML file to a file on the device.     That way, if the user isn't connected to the Internet the next time they load the app, they can still get at this information.

So how do we run it?

Of course, we can't forget the most important part: telling your application to parse the file (and giving it a web address to find it at!).

To start the process, you simply need to add this line of code to the appropriate place where you want to do the XML processing:


           [self parseXMLFileAtURL:@"http://www.webaddress.com/file.xml"];

©2014 About.com. All rights reserved.