XMLReader.m 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. //
  2. // XMLReader.m
  3. //
  4. // Created by Troy Brant on 9/18/10.
  5. // Updated by Antoine Marcadet on 9/23/11.
  6. // Updated by Divan Visagie on 2012-08-26
  7. //
  8. #import "XMLReader.h"
  9. #if !defined(__has_feature) || !__has_feature(objc_arc)
  10. #error "XMLReader requires ARC support."
  11. #endif
  12. NSString *const kXMLReaderTextNodeKey = @"text";
  13. NSString *const kXMLReaderAttributePrefix = @"@";
  14. @interface XMLReader ()
  15. @property (nonatomic, strong) NSMutableArray *dictionaryStack;
  16. @property (nonatomic, strong) NSMutableString *textInProgress;
  17. @property (nonatomic, strong) NSError *errorPointer;
  18. @end
  19. @implementation XMLReader
  20. #pragma mark - Public methods
  21. + (NSDictionary *)dictionaryForXMLData:(NSData *)data error:(NSError **)error
  22. {
  23. XMLReader *reader = [[XMLReader alloc] initWithError:error];
  24. NSDictionary *rootDictionary = [reader objectWithData:data options:0];
  25. return rootDictionary;
  26. }
  27. + (NSDictionary *)dictionaryForXMLString:(NSString *)string error:(NSError **)error
  28. {
  29. NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
  30. return [XMLReader dictionaryForXMLData:data error:error];
  31. }
  32. + (NSDictionary *)dictionaryForXMLData:(NSData *)data options:(XMLReaderOptions)options error:(NSError **)error
  33. {
  34. XMLReader *reader = [[XMLReader alloc] initWithError:error];
  35. NSDictionary *rootDictionary = [reader objectWithData:data options:options];
  36. return rootDictionary;
  37. }
  38. + (NSDictionary *)dictionaryForXMLString:(NSString *)string options:(XMLReaderOptions)options error:(NSError **)error
  39. {
  40. NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
  41. return [XMLReader dictionaryForXMLData:data options:options error:error];
  42. }
  43. #pragma mark - Parsing
  44. - (id)initWithError:(NSError **)error
  45. {
  46. self = [super init];
  47. if (self)
  48. {
  49. self.errorPointer = *error;
  50. }
  51. return self;
  52. }
  53. - (NSDictionary *)objectWithData:(NSData *)data options:(XMLReaderOptions)options
  54. {
  55. // Clear out any old data
  56. self.dictionaryStack = [[NSMutableArray alloc] init];
  57. self.textInProgress = [[NSMutableString alloc] init];
  58. // Initialize the stack with a fresh dictionary
  59. [self.dictionaryStack addObject:[NSMutableDictionary dictionary]];
  60. // Parse the XML
  61. NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
  62. [parser setShouldProcessNamespaces:(options & XMLReaderOptionsProcessNamespaces)];
  63. [parser setShouldReportNamespacePrefixes:(options & XMLReaderOptionsReportNamespacePrefixes)];
  64. [parser setShouldResolveExternalEntities:(options & XMLReaderOptionsResolveExternalEntities)];
  65. parser.delegate = self;
  66. BOOL success = [parser parse];
  67. // Return the stack's root dictionary on success
  68. if (success)
  69. {
  70. NSDictionary *resultDict = [self.dictionaryStack objectAtIndex:0];
  71. return resultDict;
  72. }
  73. return nil;
  74. }
  75. #pragma mark - NSXMLParserDelegate methods
  76. - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
  77. {
  78. // Get the dictionary for the current level in the stack
  79. NSMutableDictionary *parentDict = [self.dictionaryStack lastObject];
  80. // Create the child dictionary for the new element, and initilaize it with the attributes
  81. NSMutableDictionary *childDict = [NSMutableDictionary dictionary];
  82. [childDict addEntriesFromDictionary:attributeDict];
  83. // If there's already an item for this key, it means we need to create an array
  84. id existingValue = [parentDict objectForKey:elementName];
  85. if (existingValue)
  86. {
  87. NSMutableArray *array = nil;
  88. if ([existingValue isKindOfClass:[NSMutableArray class]])
  89. {
  90. // The array exists, so use it
  91. array = (NSMutableArray *) existingValue;
  92. }
  93. else
  94. {
  95. // Create an array if it doesn't exist
  96. array = [NSMutableArray array];
  97. [array addObject:existingValue];
  98. // Replace the child dictionary with an array of children dictionaries
  99. [parentDict setObject:array forKey:elementName];
  100. }
  101. // Add the new child dictionary to the array
  102. [array addObject:childDict];
  103. }
  104. else
  105. {
  106. // No existing value, so update the dictionary
  107. [parentDict setObject:childDict forKey:elementName];
  108. }
  109. // Update the stack
  110. [self.dictionaryStack addObject:childDict];
  111. }
  112. - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
  113. {
  114. // Update the parent dict with text info
  115. NSMutableDictionary *dictInProgress = [self.dictionaryStack lastObject];
  116. // Set the text property
  117. if ([self.textInProgress length] > 0)
  118. {
  119. // trim after concatenating
  120. NSString *trimmedString = [self.textInProgress stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
  121. [dictInProgress setObject:[trimmedString mutableCopy] forKey:kXMLReaderTextNodeKey];
  122. // Reset the text
  123. self.textInProgress = [[NSMutableString alloc] init];
  124. }
  125. // Pop the current dict
  126. [self.dictionaryStack removeLastObject];
  127. }
  128. - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
  129. {
  130. // Build the text value
  131. [self.textInProgress appendString:string];
  132. }
  133. - (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
  134. {
  135. // Set the error pointer to the parser's error object
  136. self.errorPointer = parseError;
  137. }
  138. @end