An Objective-C XML parser which runs on iOS. Based on TBXML, with enhancements of my own
Objective-C C Ruby
Switch branches/tags
Nothing to show
Clone or download
Latest commit 0f542b2 Oct 18, 2011

README.md

About

This project, TBXMLEx ("TBXML with Extensions") is based on http://www.tbxml.co.uk.

TBXML is a light-weight XML document parser written in Objective-C designed for use on Apple iPad, iPhone & iPod Touch devices. TBXML aims to provide the fastest possible XML parsing whilst utilising the fewest resources. This requirement for absolute efficiency is achieved at the expense of XML validation and modification. It is not possible to modify and generate valid XML from a TBXML object and no validation is performed whatsoever whilst importing and parsing an XML document.

The design goals for TBXML are:

  • XML files conforming to the W3C XML spec 1.0 should be passable
  • XML parsing should incur the fewest possible resources
  • XML parsing should be achieved in the shortest possible time
  • It shall be easy to write programs that utilise TBXML

How to include it in your project

There are two possible ways to include the library onto your project: by directly referencing the classes (easier) or by linking against the static library.

Option 1: Directly referencing the classes

This is the easiest approach: just copy all files from the Classes directory to your project and reference them (Add -> Existing files), and add the libz.dylib Framework (Add -> Existing frameworks).

Option 2: Link against the dynamic library

TBXMLEx is set up as a static library, so if you don't want to include the source code in your project but instead only reference the .a file, the steps are:

  • In your project, right click (or CTRL + Click) and choose Add -> Existing files
  • Navigate to the TBXMLEx directory and include the file TBXMLEx.xcodeproj. This will directly reference TBXMLEx
  • To make sure the previous step worked, expand TBXMLEx.xcodeproj, and see if libTBXMLEx.a is listed there
  • Now drag and drop libTBXMLEx.a to your project's Target, and add it to Link Binary with Libraries. Check the image below if you are unsure
  • Add the library as a dependency in your target (Target -> Get Info -> Direct Dependencies)
  • In the build settings of your target, include the full path to the Classes directory of TBXMLEx. This is a necessary, otherwise the compiler won't find the headers

Screenshot 1

Using TBXMLEx

TBXMLEx is built on top of TBXML, so if you are already using it in your projects, you will get 100% compatibiliy - even the import headers are the same. The extensions this library provides, on the other hand, are available via the header file TBXMLEx.h.

Whilte TBXML itself is great, TBXMLEx adds a thin layer on top of that in order to provide an easier to write, with a more OO friedly interface, preventing from some programming errors of happening, like infinite loops or crashes due to inexistent nodes or attributes.

Also, a very important enhancement that TBXMLEx has is that the parser will not crash if the XML is not well formed. Instead, it will just stop parsing and set the attribute invalidXML to YES. The error description, if any, will be available in the parsingErrorDescription property.

#include "TBXMLEx.h"

NSString *xml = @"<files> \
	<file timestamp='1234567890' size='123' createdAt='01/01/20011'>file1.jpg</file> \
	<file timestamp='1234567890' size='8934'> \
		<name>file2.jpg</name> \
		<attributes> \
			<createdAt>01/01/2011 13:45:56</createdAt> \
			<owner>john</owner> \
		</attributes> \
	</file> \
</files>";

TBXMLEx *xml = [TBXMLEx parserWithXML:xml];

// "files" is the rootElement
if (xml.rootElement) {
	TBXMLElementEx *fileNode = [xml.rootElement child:@"file"];

	while ([fileNode next]) {
	  // You can access the attributes through a dictionary
		NSDictionary *allAttributes = fileNode.attributes;
		NSLog(@"Timestamp: %@", [allAttributes objectForKey:@"timestamp"]);
		
		// Or you can have direct access to any specific attribute
		NSLog(@"Size: %d", [fileNode intAttribute:@"size"]);
	  
		NSObject *createdAt = [fileNode attribute:@"createdAt"];
		NSString *filename = fileNode.value; // or fileNode.text
	  
		if (!createdAt || !filename) {
			// Look for the properties someplace else
			TBXMLElementEx *nameNode = [fileNode child:@"name"];
      
			if (nameNode) {
				filename = nameNode.value;
			}
      
			TBXMLElementEx *attributesNode = [fileNode child:@"attributes"];
      
			// If an attribute does not exist it will simply return "nil", but nevertheless it's 
			// always good check if it exists if you really need it
			if (attributesNode) {
				NSLog(@"Created at: %@", [attributesNode child:@"createdAt"].value); // Will not crash if attribute is nil
				NSLog(@"Owner: %@", [attributesNode child:@"owner"].value);
			}
		}
		
		NSLog(@"Filename: %@", filename);
	}
}

Querying

TBXMLEx can do some simple object querying. Right now it is possible to directly access any given child without manually looping through all its parents, just use "/" as element separator.

NSString *xml = @"<data> \
		<a> \
			<a1/> \
			<a1/> \
			<a1/> \
		</a> \
		 \
		<b/> \
		<b/> \
		<c/> \
		 \
		<d> \
			<d1> \
				<d11/> \
				<d21/> \
			</d1> \
			 \
			<d2> \
				<d21/> \
				<d22> \
					<d221/> \
				</d22> \
			</d2> \
		</d> \
	</data>";
	
TBXMLEx *parser = [TBXMLEx parserWithXML:xml];

// Directly access the "d221" tag
NSArray *result = [parser.rootElement query:@"/d/d2/d22/d221"];
TBXMLElementEx *element = [result objectAtIndex:0];

The query method will return all elements that match the criteria, or an empty array if none is found.

API

  • Import "TBXMLEx.h"
  • Create a parser with [TBXMLEx parserWithXML:(NSString *) contents], passing the XML contents as argument. It will return an auto-released object.
  • The root element is available via the property rootElement of TBXMLEx (which is returned by parserWithXML). Use it as a starting point for everything else
  • Each element, including rootElement, is a type of TBXMLElementEx
  • The method next of TBXMLElementEx advances the pointer to the next available element. Use it to loop through the elements
  • To get the attributes of a given element, you can either get them all at once by calling the attributes property (like someNode.attributes), or accessing them directly using any of the helper methods: attribute:(NSString *), intAttribute:(NSString *) or longAttribute:(NSString *)
  • To access the value of a tag or its CDATA, you can use either text or value
  • If the XML is not well formed, the property invalidXML of TBXMLEx will be set to YES, and the error description (if any) will be available through the parsingErrorDescription property.
  • To get a child element, use the method child:(NSString *) from TBXMLElementEx. Use if even if you have more than one child, and use the next method to navigate through the elements.

Contributing to the project

Enhancements and fixes are very welcome. In order to do, please fork the project (instructions at http://help.github.com/fork-a-repo/), do your changes, and then send a pull request (instructions at http://help.github.com/send-pull-requests/).

Test cases

TBXMLEx uses GHUnit as unit testing framework, which is included as submodule. Haowever, the compiled framework is also available so you won't need to compile it by hand. If - for any reason - you wish to do so, run git submodule init and follow the instructions at http://longweekendmobile.com/2011/02/23/tdd-best-practices-testing-in-ios4-with-ghunit-part-1.

To run the tests, change the Active Target in XCode to Tests, and click Build and Run. The tests run on the Simulator.