XPathQuery4ObjC

Introduction

XPathQuery4ObjC is a wrapper class for NSXMLParser to query XML documents such as Web API responses with XPath more easily.

I know that some wrapper classes for Objective-C was already released such as XPathQuery. However, these are not appropriate to use for me because I would like to query more directly using XPath and these wrappers have some bugs frown

I have developed other my original DOM parsers which are implemented using some native XML parsers such as Xerese, libxml2 and expat on MacOSX and iOS platforms, but I decided to use NSXMLParser to implement CGXPathQuery because I know that it is difficult to support all XML features using the native XML parsers. Please check CyberLinkForC, or CyberLinkForCC and CyberX3DForCC if you want to know other my DOM parsers in more detail.

For example, you can query XML element nodes in Web API responses such as Media RSS using the CGXPathQuery as the following.

NSURL *rssURL = [NSURL URLWithString:@"http://rss.news.yahoo.com/rss/topstories"];
CGXPathQuery *xpathQuery = [[CGXPathQuery alloc] initWithContentsOfURL:rssURL];

if ([xpathQuery parse] == YES) {
    NSString *title = [xpathQuery valueForXPath:@"/rss/channel/title"];
        ..........
    NSArray *entries = [xpathQuery objectsForXPath:@"/rss/channel/item"];
    for (CGXPathObject *xpathObject in entries) {
        NSString *entryTitle = [xpathObject valueForXPath:@"title"];
            ..........
        NSURL *linkUrl = [NSURL URLWithString:[xpathObject valueForXPath:@"link"]];
            ..........
        NSString *imageUrl = nil;
        CGXPathObject *mediaContent = [xpathObject objectForXPath:@"media:content"];
        if (mediaContent != nil) {
            imageUrl = [NSURL URLWithString:[[mediaContent attributes] valueForKey:@"url"]];
        }
            ..........
    }
}
xpathquery_ios_rsssample.png

Installation

To use CGXPathQuery on your XCode project, you have to only add the two files, CGXPathQuery.h and CGXPathQuety.m, into your project smile

Classes

CGXPathQuery is composed of the following two classes, CGXPathQuery and CGXPathObject.

CGXPathQuery

CGXPathQuery is a base class to parse XML documents, you have to initialize with the XML data or the URL. You have to use absolute XPaths to pass the methods such as objectForXPath:.

@interface CGXPathQuery : NSObject {
}
@property(retain) NSError *parserError;
- (id)initWithContentsOfURL:(NSURL *)url;
- (id)initWithData:(NSData *)data;
- (BOOL)parse;
- (NSArray *)objectsForXPath:(NSString *)absoluteXPath;
- (CGXPathObject *)objectForXPath:(NSString *)absoluteXPath;
- (NSArray *)valuesForXPath:(NSString *)absoluteXPath;
- (NSString *)valueForXPath:(NSString *)absoluteXPath;
@end

CGXPathObject

CGXPathObject is a base object of CGXPathQuery response. CGXPathObject extends NSDictionary to add some useful methods using a category interface. You have to use relative XPaths to pass the methods such as objectForXPath:.

#define CGXPathObject NSDictionary
@interface CGXPathObject(CGXPathQuery)
- (NSArray *)children;
- (NSString *)name;
- (NSString *)value;
- (NSDictionary *)attributes;
- (NSArray *)objectsForXPath:(NSString *)relativeXPath;
- (CGXPathObject *)objectForXPath:(NSString *)relativeXPath;
- (NSArray *)valuesForXPath:(NSString *)relativeXPath;
- (NSString *)valueForXPath:(NSString *)relativeXPath;
@end

Limitation

CGXPathQuery supports only the following simple XPath specification which is minimal subset of XPath, and it doesn't support the complex specifications currently.

LocationPath ::= RelativeLocationPath   
                 | AbsoluteLocationPath

AbsoluteLocationPath ::= '/' RelativeLocationPath

RelativeLocationPath ::= ElementName   
                       | RelativeLocationPath '/' ElementName

ElementName ::= [a-zA-Z0-9]+

Additionally, CGXPathQuery doesn't support traversal queries which jumps the parent element. For example, the following code get only a first title element instead of all title elements..

XML document Inappropriate Code
<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" …..>
…..
<item>
    <title>…..</title>
    .....
</item>
<item>
    <title>…..</title>
    .....
</item>
</rss>
CGXPathQuery *xpathQuery = [[CGXPathQuery alloc] initWithContentsOfURL:rssURL];
if ([xpathQuery parse] == YES) {
    NSArray *itemTitles = [xpathQuery objectsForXPath:@"/rss/channel/item/title"];
    for (CGXPathObject *titleObject in itemTitles) {
        NSString *title = [titleObject value];
        .....
    }
}

To get all title elements normally, use the following steps.

CGXPathQuery *xpathQuery = [[CGXPathQuery alloc] initWithContentsOfURL:rssURL];
if ([xpathQuery parse] == YES) {
    NSArray *items = [xpathQuery objectsForXPath:@"/rss/channel/item"];
    for (CGXPathObject *itemObject in entries) {
        NSString *title = [itemObject valueForXPath:@"title"];
        .....
    }
}

Resources

Repositories

Please see the project page on GitHub to get the source codes with the examples such as a simple RSS reader smile

GitHub https://github.com/cybergarage/XPathQuery4ObjC
Doxygen http://www.cybergarage.org/doxygen/yaml4objc/
Topic revision: r9 - 2011-06-21 - 23:30:13 - SatoshiKonno
 

Copyright © 2012 by Satoshi Konno Powerd by TWiki logoTWiki.