Posts Tagged ‘dom’

The idea here is to parse the XML response from a Wunderground Data Feed Web Service, which provides information for current weather conditions. The conceptual part of target XML follows. More details about XML and available weather API can be found on the Wunderground Data Feed site.

<current_observation>
  <credit>Weather Underground NOAA Weather Station</credit>
  <credit_URL>http://wunderground.com/</credit_URL>
  <image>
    <url>http://icons.wunderground.com/graphics/wu2/logo_130x80.png</url>
    <title>Weather Underground</title>
    <link>http://wunderground.com/</link>
  </image>
  +<display_location></display_location>
  +<observation_location></observation_location>
  <station_id>KSFO</station_id>
  <observation_time>Last Updated on July 14, 9:56 AM PDT</observation_time>
  ...
  <local_time>July 14, 10:01 AM PDT</local_time>
  ...
  <local_epoch>1247590877</local_epoch>
  <weather>fog</weather>
  <temperature_string>52 F (11 C)</temperature_string>
  <temp_f>52</temp_f>
  <temp_c>11</temp_c>
  <relative_humidity>76%</relative_humidity>
  ...
  <icon>fog</icon>
  ...
</current_observation>

For processing this XML we decided to use DOM parser for the following reasons:

  • To save the structure of data (more flexible usage for presentation and further processing)
  • To ensure good maintainability and adaptability (external provider may change XML structure)
  • The XML is not so large that it would not cause memory issues.
Figure 1 the DOM presents an XML document as a tree-structure

DOM Tree

The DOM presents an XML document as a tree-structure. The parser creates a tree object out of the document. The user accesses data by traversing the tree. The API allows for constructing, accessing and manipulating the structure and content of XML documents (Figure 2).

Figure 2 Using a DOM Tree.

For our implementation we use a very simple and effective parser - XMLTreeParser/XMLTreeNode. The implementation consists of two classes: XMLTreeParser and XMLTreeNode. It uses NSXMLParser to read the document and for every one <element> tag creates a XMLTreeNode object. That way, it creates the tree that contains nodes of type XMLTreeNode.

To create a parser, simply instantiate the XMLTreeParser class:

XMLTreeParser* parser = [[XMLTreeParser alloc] init];

After that start parsing – call the parse method and provide the XML data:

&lt;strong&gt;XMLTreeNode* root = [parser parse:xmlData];

Now you can traverse through the tree manually looking for things.

NSArray* items = [stuffindChildren:@&quot;icon&quot;];

Architecture of an XML application using DOM

The DOM model is easiest to understand. On Figure 3, a basic architecture of an XML application using DOM is presented:

Figure 3 Architecture of an XML application using DOM

So here’s what happens: A parser reads the XML file and builds a DOM document to match the XML file. From that point until a save is performed, all interaction between the application and XML hits the DOM document rather than the corresponding XML file. It’s interesting to note that almost all XML parsers use SAX. Before you build a DOM document you must detect events such as the start of an element (start tag encountered), end of an element (end tag encountered) and/or a new attribute (name followed by equal sign followed by quoted string encountered). DOM can be thought of as an extra abstraction to lessen the programmer’s workload, at the expense of memory usage.

Modifications are made directly to the DOM document. Elements can be added, deleted, renamed, and rearranged. Text nodes can be added, delete,d or changed. Elements can be moved either within the same level, or promoted or demoted to different levels.

Choose a design pattern for implementation

Choosing the appropriate pattern is a critical step. The Dynamic Document architectural pattern was used in our case. This pattern contains XML not typed by DTD or schema, but follows assessors for underlying program objects. It allows for unlimited extension by multiple, uncoordinated parties at the cost of lack of type-checking, and it’s simple to implement.

Figure 4 Map the XML document to object model.

The DOM API is used to read information from an XML document. There is an easier way of getting around using DOM for modifying and saving the XML data — creating an object model for the information in the document. You can create this object model by giving it a DOM object that holds all the XML document information. That is, the XML document should be mapped to an object model.

Figure 4 shows how to map the Current Observation XML document to a set of related classes (object model). The <current_observation> element is mapped to the CurrentObservation class; the <display_location> element is mapped to the DisplayLocation class. The <observation_location> element is mapped to the ObservationLocation class. Single elements (without sub elements or others attributes) are mapped to the parent element class properties.  Each class can be instantiated using a method or Factory implementation by providing a part of XML tree, with root – class element. In this the exampleinit method was used -(id)initWithData:(NSData *)data;.

Here is a partial code listing from CurrentObservation.h:

  @interface WUCurrWeather : NSObject {
   NSString *credit;
   ...
   Image *image;
   DisplayLocation *displayLocation;
   ...
   NSString *icon;
  ...
  }

  @property(nonatomic, retain) NSString *credit;
  @property(nonatomic, retain) Image *image;
  @property(nonatomic, retain) DisplayLocation *displayLocation;
  ...
  @property(nonatomic, retain) NSString *icon;
  ...

  - (id)initWithData:(NSData *)data;

  -(NSString *)description;
  @end;
and Image.h files

@interface Image : NSObject {
NSString *url;
NSString *title;
NSString *link;
}
@property(nonatomic, retain) NSString *url;
@property(nonatomic, retain) NSString *title;
@property(nonatomic, retain) NSString *link;
- (id)initWidthData:(XMLTreeNode *)root;
- (NSString *) description;
@end
Now you can use your object model to manage data as you wish – save it in a database or show it in a GUI for example.

Example

Below is an example of a View Based iPhone Application that calls a Wunderground Data Feed Web Service to get current weather conditions. The update button repeats the call and refreshes the screen when new data arrives. The screenshots below visualize the application’s behavior:

In the provided source code you can find all these steps implemented.

Existing APIs for XML processing fall into two categories:

  • SAX (Simple API for XML) is a serial access parser API for XML. It provides a mechanism for reading data from an XML document. The quantity of memory that a SAX parser must use in order to function is typically much smaller than that of a DOM parser. Unlike DOM, there is no formal specification for SAX.
  • The Document Object Model (DOM) is a cross-platform and language-independent convention for representing and interacting with objects in HTML, XHTML and XML documents. It represents a tree structure based API. The Dom parser implements the DOM API and it creates a DOM tree in memory for a XML document. Because DOM supports navigation in any direction (e.g., parent and previous sibling) and allows for arbitrary modifications, an implementation must at least buffer the document that has been read so far (or some parsed form of it).

Cocoa offers a “complete” XML parser with the NSXML family of classes.

  • NSXMLParser is a SAX parser, meaning that it traverses the XML tree and informs a delegate of events as they happen. This is an event-driven parser, which calls methods on a delegate to handle “events” as it parses through the XML.
  • NSXMLDocument provides tree parser functionality. An instance of NSXMLDocument represents an XML document as internalized into a logical tree structure. This tree-based parser is fairly sophisticated but it is not included in Cocoa-Touch.

In the spirit of small and simple, iPhone developers have the NSXMLParser class to use. Because NSXMLParser only generates messages for a delegate, and doesn’t store data about the XML tree, it requires less memory and is better for accessing small pieces of data or parsing large XML documents. The basic operation of the NSXMLParser class will not be covered here. This is well documented in the Apple Programming Guide.

For devices like iPhone where memory usage is critical, using tree parser is not recommended. I guess that is why Apple doesn’t provide NSXMLDocument on iPhone/iPod touch. However, the model of event parsing may not be acceptable by your code. NSXMLParser is probably adequate, but it is not going to handle all your XML parsing needs. The biggest problem with this type of streaming parser is that you lose the structure of your data; your call-backs do not provide any context or hierarchy. Because of the above mentioned or by some other reason like often changing XML format [1] you may need to use something like DOM model implementation.

Available Libraries (DOM model implementations) for iPhone

Which parser (DOM or SAX) should you use?

Well, if you’re going to need to parse very large XML documents, then you should probably consider NSXMLParser. What “very large” means depends a lot on the available memory you have to work with? The model of iPhone and the memory already in use by your app may prevent you from being able to use DOM model.

While DOM model usage may seem easier, NSXMLParser really excels in situations where you’re loading in many instances of the same kind of data, such as an address book or twitter feed. However, if memory is not an issue, I would recommend DOM model. It makes it easy to access the data you want and also allows you to manipulate the tree.

References

  1. Arash Payan(Salman),APXML: NSXMLDocument ’substitute’ for iPhone/iPod Touch – Arash Payan,Sep 27th, 2009
  2. jongampark, Tree XML parser for iPhone, 2009