XPathQuery.m 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. //
  2. // XPathQuery.m
  3. // FuelFinder
  4. //
  5. // Created by Matt Gallagher on 4/08/08.
  6. // Copyright 2008 __MyCompanyName__. All rights reserved.
  7. //
  8. #import "XPathQuery.h"
  9. #import <libxml/tree.h>
  10. #import <libxml/parser.h>
  11. #import <libxml/HTMLparser.h>
  12. #import <libxml/xpath.h>
  13. #import <libxml/xpathInternals.h>
  14. NSDictionary *DictionaryForNode(xmlNodePtr currentNode, NSMutableDictionary *parentResult,BOOL parentContent);
  15. NSArray *PerformXPathQuery(xmlDocPtr doc, NSString *query);
  16. NSDictionary *DictionaryForNode(xmlNodePtr currentNode, NSMutableDictionary *parentResult,BOOL parentContent)
  17. {
  18. NSMutableDictionary *resultForNode = [NSMutableDictionary dictionary];
  19. if (currentNode->name) {
  20. NSString *currentNodeContent = [NSString stringWithCString:(const char *)currentNode->name
  21. encoding:NSUTF8StringEncoding];
  22. resultForNode[@"nodeName"] = currentNodeContent;
  23. }
  24. xmlChar *nodeContent = xmlNodeGetContent(currentNode);
  25. if (nodeContent != NULL) {
  26. NSString *currentNodeContent = [NSString stringWithCString:(const char *)nodeContent
  27. encoding:NSUTF8StringEncoding];
  28. if ([resultForNode[@"nodeName"] isEqual:@"text"] && parentResult) {
  29. if (parentContent) {
  30. NSCharacterSet *charactersToTrim = [NSCharacterSet whitespaceAndNewlineCharacterSet];
  31. parentResult[@"nodeContent"] = [currentNodeContent stringByTrimmingCharactersInSet:charactersToTrim];
  32. return nil;
  33. }
  34. if (currentNodeContent != nil) {
  35. resultForNode[@"nodeContent"] = currentNodeContent;
  36. }
  37. return resultForNode;
  38. } else {
  39. resultForNode[@"nodeContent"] = currentNodeContent;
  40. }
  41. xmlFree(nodeContent);
  42. }
  43. xmlAttr *attribute = currentNode->properties;
  44. if (attribute) {
  45. NSMutableArray *attributeArray = [NSMutableArray array];
  46. while (attribute) {
  47. NSMutableDictionary *attributeDictionary = [NSMutableDictionary dictionary];
  48. NSString *attributeName = [NSString stringWithCString:(const char *)attribute->name
  49. encoding:NSUTF8StringEncoding];
  50. if (attributeName) {
  51. attributeDictionary[@"attributeName"] = attributeName;
  52. }
  53. if (attribute->children) {
  54. NSDictionary *childDictionary = DictionaryForNode(attribute->children, attributeDictionary, true);
  55. if (childDictionary) {
  56. attributeDictionary[@"attributeContent"] = childDictionary;
  57. }
  58. }
  59. if ([attributeDictionary count] > 0) {
  60. [attributeArray addObject:attributeDictionary];
  61. }
  62. attribute = attribute->next;
  63. }
  64. if ([attributeArray count] > 0) {
  65. resultForNode[@"nodeAttributeArray"] = attributeArray;
  66. }
  67. }
  68. xmlNodePtr childNode = currentNode->children;
  69. if (childNode) {
  70. NSMutableArray *childContentArray = [NSMutableArray array];
  71. while (childNode) {
  72. NSDictionary *childDictionary = DictionaryForNode(childNode, resultForNode,false);
  73. if (childDictionary) {
  74. [childContentArray addObject:childDictionary];
  75. }
  76. childNode = childNode->next;
  77. }
  78. if ([childContentArray count] > 0) {
  79. resultForNode[@"nodeChildArray"] = childContentArray;
  80. }
  81. }
  82. xmlBufferPtr buffer = xmlBufferCreate();
  83. xmlNodeDump(buffer, currentNode->doc, currentNode, 0, 0);
  84. NSString *rawContent = [NSString stringWithCString:(const char *)buffer->content encoding:NSUTF8StringEncoding];
  85. if (rawContent != nil) {
  86. resultForNode[@"raw"] = rawContent;
  87. }
  88. xmlBufferFree(buffer);
  89. return resultForNode;
  90. }
  91. NSArray *PerformXPathQuery(xmlDocPtr doc, NSString *query)
  92. {
  93. xmlXPathContextPtr xpathCtx;
  94. xmlXPathObjectPtr xpathObj;
  95. /* Make sure that passed query is non-nil and is NSString object */
  96. if (query == nil || ![query isKindOfClass:[NSString class]]) {
  97. return nil;
  98. }
  99. /* Create xpath evaluation context */
  100. xpathCtx = xmlXPathNewContext(doc);
  101. if(xpathCtx == NULL) {
  102. NSLog(@"Unable to create XPath context.");
  103. return nil;
  104. }
  105. /* Evaluate xpath expression */
  106. xpathObj = xmlXPathEvalExpression((xmlChar *)[query cStringUsingEncoding:NSUTF8StringEncoding], xpathCtx);
  107. if(xpathObj == NULL) {
  108. NSLog(@"Unable to evaluate XPath.");
  109. xmlXPathFreeContext(xpathCtx);
  110. return nil;
  111. }
  112. xmlNodeSetPtr nodes = xpathObj->nodesetval;
  113. if (!nodes) {
  114. NSLog(@"Nodes was nil.");
  115. xmlXPathFreeObject(xpathObj);
  116. xmlXPathFreeContext(xpathCtx);
  117. return nil;
  118. }
  119. NSMutableArray *resultNodes = [NSMutableArray array];
  120. for (NSInteger i = 0; i < nodes->nodeNr; i++) {
  121. NSDictionary *nodeDictionary = DictionaryForNode(nodes->nodeTab[i], nil,false);
  122. if (nodeDictionary) {
  123. [resultNodes addObject:nodeDictionary];
  124. }
  125. }
  126. /* Cleanup */
  127. xmlXPathFreeObject(xpathObj);
  128. xmlXPathFreeContext(xpathCtx);
  129. return resultNodes;
  130. }
  131. NSArray *PerformHTMLXPathQuery(NSData *document, NSString *query) {
  132. return PerformHTMLXPathQueryWithEncoding(document, query, nil);
  133. }
  134. NSArray *PerformHTMLXPathQueryWithEncoding(NSData *document, NSString *query,NSString *encoding)
  135. {
  136. xmlDocPtr doc;
  137. /* Load XML document */
  138. const char *encoded = encoding ? [encoding cStringUsingEncoding:NSUTF8StringEncoding] : NULL;
  139. doc = htmlReadMemory([document bytes], (int)[document length], "", encoded, HTML_PARSE_NOWARNING | HTML_PARSE_NOERROR);
  140. if (doc == NULL) {
  141. NSLog(@"Unable to parse.");
  142. return nil;
  143. }
  144. NSArray *result = PerformXPathQuery(doc, query);
  145. xmlFreeDoc(doc);
  146. return result;
  147. }
  148. NSArray *PerformXMLXPathQuery(NSData *document, NSString *query) {
  149. return PerformXMLXPathQueryWithEncoding(document, query, nil);
  150. }
  151. NSArray *PerformXMLXPathQueryWithEncoding(NSData *document, NSString *query,NSString *encoding)
  152. {
  153. xmlDocPtr doc;
  154. /* Load XML document */
  155. const char *encoded = encoding ? [encoding cStringUsingEncoding:NSUTF8StringEncoding] : NULL;
  156. doc = xmlReadMemory([document bytes], (int)[document length], "", encoded, XML_PARSE_RECOVER);
  157. if (doc == NULL) {
  158. NSLog(@"Unable to parse.");
  159. return nil;
  160. }
  161. NSArray *result = PerformXPathQuery(doc, query);
  162. xmlFreeDoc(doc);
  163. return result;
  164. }