123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- //
- // XMLReader.m
- //
- // Created by Troy Brant on 9/18/10.
- // Updated by Antoine Marcadet on 9/23/11.
- // Updated by Divan Visagie on 2012-08-26
- //
- #import "XMLReader.h"
- #if !defined(__has_feature) || !__has_feature(objc_arc)
- #error "XMLReader requires ARC support."
- #endif
- NSString *const kXMLReaderTextNodeKey = @"text";
- NSString *const kXMLReaderAttributePrefix = @"@";
- @interface XMLReader ()
- @property (nonatomic, strong) NSMutableArray *dictionaryStack;
- @property (nonatomic, strong) NSMutableString *textInProgress;
- @property (nonatomic, strong) NSError *errorPointer;
- @end
- @implementation XMLReader
- #pragma mark - Public methods
- + (NSDictionary *)dictionaryForXMLData:(NSData *)data error:(NSError **)error
- {
- XMLReader *reader = [[XMLReader alloc] initWithError:error];
- NSDictionary *rootDictionary = [reader objectWithData:data options:0];
- return rootDictionary;
- }
- + (NSDictionary *)dictionaryForXMLString:(NSString *)string error:(NSError **)error
- {
- NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
- return [XMLReader dictionaryForXMLData:data error:error];
- }
- + (NSDictionary *)dictionaryForXMLData:(NSData *)data options:(XMLReaderOptions)options error:(NSError **)error
- {
- XMLReader *reader = [[XMLReader alloc] initWithError:error];
- NSDictionary *rootDictionary = [reader objectWithData:data options:options];
- return rootDictionary;
- }
- + (NSDictionary *)dictionaryForXMLString:(NSString *)string options:(XMLReaderOptions)options error:(NSError **)error
- {
- NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
- return [XMLReader dictionaryForXMLData:data options:options error:error];
- }
- #pragma mark - Parsing
- - (id)initWithError:(NSError **)error
- {
- self = [super init];
- if (self)
- {
- self.errorPointer = *error;
- }
- return self;
- }
- - (NSDictionary *)objectWithData:(NSData *)data options:(XMLReaderOptions)options
- {
- // Clear out any old data
- self.dictionaryStack = [[NSMutableArray alloc] init];
- self.textInProgress = [[NSMutableString alloc] init];
-
- // Initialize the stack with a fresh dictionary
- [self.dictionaryStack addObject:[NSMutableDictionary dictionary]];
-
- // Parse the XML
- NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
-
- [parser setShouldProcessNamespaces:(options & XMLReaderOptionsProcessNamespaces)];
- [parser setShouldReportNamespacePrefixes:(options & XMLReaderOptionsReportNamespacePrefixes)];
- [parser setShouldResolveExternalEntities:(options & XMLReaderOptionsResolveExternalEntities)];
-
- parser.delegate = self;
- BOOL success = [parser parse];
-
- // Return the stack's root dictionary on success
- if (success)
- {
- NSDictionary *resultDict = [self.dictionaryStack objectAtIndex:0];
- return resultDict;
- }
-
- return nil;
- }
- #pragma mark - NSXMLParserDelegate methods
- - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
- {
- // Get the dictionary for the current level in the stack
- NSMutableDictionary *parentDict = [self.dictionaryStack lastObject];
- // Create the child dictionary for the new element, and initilaize it with the attributes
- NSMutableDictionary *childDict = [NSMutableDictionary dictionary];
- [childDict addEntriesFromDictionary:attributeDict];
-
- // If there's already an item for this key, it means we need to create an array
- id existingValue = [parentDict objectForKey:elementName];
- if (existingValue)
- {
- NSMutableArray *array = nil;
- if ([existingValue isKindOfClass:[NSMutableArray class]])
- {
- // The array exists, so use it
- array = (NSMutableArray *) existingValue;
- }
- else
- {
- // Create an array if it doesn't exist
- array = [NSMutableArray array];
- [array addObject:existingValue];
- // Replace the child dictionary with an array of children dictionaries
- [parentDict setObject:array forKey:elementName];
- }
-
- // Add the new child dictionary to the array
- [array addObject:childDict];
- }
- else
- {
- // No existing value, so update the dictionary
- [parentDict setObject:childDict forKey:elementName];
- }
-
- // Update the stack
- [self.dictionaryStack addObject:childDict];
- }
- - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
- {
- // Update the parent dict with text info
- NSMutableDictionary *dictInProgress = [self.dictionaryStack lastObject];
-
- // Set the text property
- if ([self.textInProgress length] > 0)
- {
- // trim after concatenating
- NSString *trimmedString = [self.textInProgress stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
- [dictInProgress setObject:[trimmedString mutableCopy] forKey:kXMLReaderTextNodeKey];
- // Reset the text
- self.textInProgress = [[NSMutableString alloc] init];
- }
-
- // Pop the current dict
- [self.dictionaryStack removeLastObject];
- }
- - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
- {
- // Build the text value
- [self.textInProgress appendString:string];
- }
- - (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
- {
- // Set the error pointer to the parser's error object
- self.errorPointer = parseError;
- }
- @end
|