Skip to content

Instantly share code, notes, and snippets.

@futuretap
Created July 12, 2011 14:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save futuretap/1078142 to your computer and use it in GitHub Desktop.
Save futuretap/1078142 to your computer and use it in GitHub Desktop.
//
// XPathQuery.m
// FuelFinder
//
// Created by Matt Gallagher on 4/08/08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
// Permission is given to use this source code file, free of charge, in any
// project, commercial or otherwise, entirely at your risk, with the condition
// that any redistribution (in part or whole) of source code must retain
// this copyright and permission notice. Attribution in compiled projects is
// appreciated but not required.
//
#import "XPathQuery.h"
#import <libxml/tree.h>
#import <libxml/parser.h>
#import <libxml/HTMLparser.h>
#import <libxml/xpath.h>
#import <libxml/xpathInternals.h>
NSDictionary *DictionaryForNode(xmlNodePtr currentNode, NSMutableDictionary *parentResult);
NSArray *PerformXPathQuery(xmlDocPtr doc, NSString *query);
NSDictionary *DictionaryForNode(xmlNodePtr currentNode, NSMutableDictionary *parentResult)
{
NSMutableDictionary *resultForNode = [NSMutableDictionary dictionary];
if (currentNode->name)
{
NSString *currentNodeContent =
[NSString stringWithCString:(const char *)currentNode->name encoding:NSUTF8StringEncoding];
[resultForNode setObject:currentNodeContent forKey:@"nodeName"];
}
if (currentNode->content && currentNode->type != XML_DOCUMENT_TYPE_NODE)
{
NSString *currentNodeContent =
[NSString stringWithCString:(const char *)currentNode->content encoding:NSUTF8StringEncoding];
if ([[resultForNode objectForKey:@"nodeName"] isEqual:@"text"] && parentResult)
{
currentNodeContent = [currentNodeContent
stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSString *existingContent = [parentResult objectForKey:@"nodeContent"];
NSString *newContent;
if (existingContent)
{
newContent = [existingContent stringByAppendingString:currentNodeContent];
}
else
{
newContent = currentNodeContent;
}
[parentResult setObject:newContent forKey:@"nodeContent"];
return nil;
}
[resultForNode setObject:currentNodeContent forKey:@"nodeContent"];
}
xmlAttr *attribute = currentNode->properties;
if (attribute)
{
NSMutableArray *attributeArray = [NSMutableArray array];
while (attribute)
{
NSMutableDictionary *attributeDictionary = [NSMutableDictionary dictionary];
NSString *attributeName =
[NSString stringWithCString:(const char *)attribute->name encoding:NSUTF8StringEncoding];
if (attributeName)
{
[attributeDictionary setObject:attributeName forKey:@"attributeName"];
}
if (attribute->children)
{
NSDictionary *childDictionary = DictionaryForNode(attribute->children, attributeDictionary);
if (childDictionary)
{
[attributeDictionary setObject:childDictionary forKey:@"attributeContent"];
}
}
if ([attributeDictionary count] > 0)
{
[attributeArray addObject:attributeDictionary];
}
attribute = attribute->next;
}
if ([attributeArray count] > 0)
{
[resultForNode setObject:attributeArray forKey:@"nodeAttributeArray"];
}
}
xmlNodePtr childNode = currentNode->children;
if (childNode)
{
NSMutableArray *childContentArray = [NSMutableArray array];
while (childNode)
{
NSDictionary *childDictionary = DictionaryForNode(childNode, resultForNode);
if (childDictionary)
{
[childContentArray addObject:childDictionary];
}
childNode = childNode->next;
}
if ([childContentArray count] > 0)
{
[resultForNode setObject:childContentArray forKey:@"nodeChildArray"];
}
}
return resultForNode;
}
NSArray *PerformXPathQuery(xmlDocPtr doc, NSString *query)
{
xmlXPathContextPtr xpathCtx;
xmlXPathObjectPtr xpathObj;
/* Create xpath evaluation context */
xpathCtx = xmlXPathNewContext(doc);
if(xpathCtx == NULL)
{
NSLog(@"Unable to create XPath context.");
return nil;
}
/* Evaluate xpath expression */
xpathObj = xmlXPathEvalExpression((xmlChar *)[query cStringUsingEncoding:NSUTF8StringEncoding], xpathCtx);
if(xpathObj == NULL) {
NSLog(@"Unable to evaluate XPath.");
/* Cleanup */
xmlXPathFreeContext(xpathCtx);
return nil;
}
xmlNodeSetPtr nodes = xpathObj->nodesetval;
if (!nodes)
{
/* Cleanup */
xmlXPathFreeObject(xpathObj);
xmlXPathFreeContext(xpathCtx);
return nil;
}
NSMutableArray *resultNodes = [NSMutableArray array];
for (NSInteger i = 0; i < nodes->nodeNr; i++)
{
NSDictionary *nodeDictionary = DictionaryForNode(nodes->nodeTab[i], nil);
if (nodeDictionary)
{
[resultNodes addObject:nodeDictionary];
}
}
/* Cleanup */
xmlXPathFreeObject(xpathObj);
xmlXPathFreeContext(xpathCtx);
return resultNodes;
}
NSArray *PerformHTMLXPathQuery(NSData *document, NSString *query)
{
xmlDocPtr doc;
/* Load XML document */
doc = htmlReadMemory([document bytes], [document length], "", NULL, HTML_PARSE_NOWARNING | HTML_PARSE_NOERROR);
if (doc == NULL)
{
NSLog(@"Unable to parse.");
return nil;
}
NSArray *result = PerformXPathQuery(doc, query);
xmlFreeDoc(doc);
return result;
}
NSArray *PerformXMLXPathQuery(NSData *document, NSString *query)
{
xmlDocPtr doc;
/* Load XML document */
doc = xmlReadMemory([document bytes], [document length], "", NULL, XML_PARSE_RECOVER);
if (doc == NULL)
{
NSLog(@"Unable to parse.");
return nil;
}
NSArray *result = PerformXPathQuery(doc, query);
xmlFreeDoc(doc);
return result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment