Skip to content

Commit

Permalink
Allow collision keys in the XML parser in order to support RSS feeds.
Browse files Browse the repository at this point in the history
RSS feeds tend to allow their feed items to be placed in the channel
object as flat items, instead of within an array. The original XML parser
implementation assumed that keys were always unique, but this is not the
case with RSS feeds. Options were added to make it possible to toggle this
functionality.
  • Loading branch information
jverkoey committed Feb 18, 2010
1 parent 9f6a6a0 commit 7183db6
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 9 deletions.
8 changes: 6 additions & 2 deletions src/TTURLXMLResponse.m
Expand Up @@ -26,7 +26,8 @@
///////////////////////////////////////////////////////////////////////////////////////////////////
@implementation TTURLXMLResponse

@synthesize rootObject = _rootObject;
@synthesize rootObject = _rootObject;
@synthesize isRssFeed = _isRssFeed;


///////////////////////////////////////////////////////////////////////////////////////////////////
Expand All @@ -52,9 +53,12 @@ - (NSError*)request:(TTURLRequest*)request processResponse:(NSHTTPURLResponse*)r
TTDASSERT(nil == _rootObject);

if ([data isKindOfClass:[NSData class]]) {
NSLog(@"Data: %@", [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]);
//NSLog(@"Data: %@", [[[NSString alloc]
// initWithData: data
// encoding: NSUTF8StringEncoding] autorelease]);
TTXMLParser* parser = [[TTXMLParser alloc] initWithData:data];
parser.delegate = self;
parser.treatDuplicateKeysAsArrayItems = self.isRssFeed;
[parser parse];
_rootObject = [parser.rootObject retain];
TT_RELEASE_SAFELY(parser);
Expand Down
42 changes: 38 additions & 4 deletions src/TTXMLParser.m
Expand Up @@ -36,7 +36,8 @@
///////////////////////////////////////////////////////////////////////////////////////////////////
@implementation TTXMLParser

@synthesize rootObject = _rootObject;
@synthesize rootObject = _rootObject;
@synthesize treatDuplicateKeysAsArrayItems = _treatDuplicateKeysAsArrayItems;


///////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -82,8 +83,8 @@ - (id)allocObjectForElementName: (NSString*) elementName

///////////////////////////////////////////////////////////////////////////////////////////////////
- (void)addChild:(id)childObject toObject:(id)object {
// Is this an internal common "array" type?

// Is this an internal common "array" type?
if ([object isKindOfClass:[NSDictionary class]] &&
[[object objectForKey:kPrivateKey_EntityType] isEqualToString:kCommonType_Array]) {

Expand All @@ -92,10 +93,37 @@ - (void)addChild:(id)childObject toObject:(id)object {
[[object objectForKey:kPrivateKey_Array] addObject:childObject];
}

// Is it an unknown dictionary type?
} else if ([object isKindOfClass:[NSDictionary class]] &&
[[object objectForKey:kPrivateKey_EntityType] isEqualToString:kCommonXMLType_Unknown]) {
// It's an unknown dictionary type, let's just add this object then.
[object setObject:childObject forKey:[childObject objectForKey:kPrivateKey_EntityName]];

if (self.treatDuplicateKeysAsArrayItems) {
NSString* entityName = [childObject objectForKey:kPrivateKey_EntityName];
id entityObject = [object objectForKey:entityName];
if (nil == entityObject) {
// No collision, add it!
[object setObject:childObject forKey:entityName];

} else {
// Collision, check if it's already an array.
if (TTIsArrayWithItems(entityObject)) {
[entityObject addObject:childObject];
} else {
NSMutableArray* array = [[NSMutableArray alloc] init];
[array addObject:entityObject];
[array addObject:childObject];
[object setObject:array forKey:entityName];
TT_RELEASE_SAFELY(array);
}
}

} else {
// Avoid overwriting existing keys.
// If this is asserting, you probably need treatDuplicateKeysAsArrayItems set to YES.
TTDASSERT(nil == [object objectForKey:[childObject objectForKey:kPrivateKey_EntityName]]);

[object setObject:childObject forKey:[childObject objectForKey:kPrivateKey_EntityName]];
}
}
}

Expand Down Expand Up @@ -215,6 +243,12 @@ - (void) parser: (NSXMLParser*) parser
}


///////////////////////////////////////////////////////////////////////////////////////////////////
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
TTDERROR(@"Error parsing the XML: %@", [parseError localizedDescription]);
}


@end


Expand Down
13 changes: 11 additions & 2 deletions src/Three20/TTURLXMLResponse.h
Expand Up @@ -23,9 +23,18 @@
* parse HTML pages that are likely to have invalid markup.
*/
@interface TTURLXMLResponse : NSObject <TTURLResponse> {
id _rootObject;
id _rootObject;
BOOL _isRssFeed;
}

@property (nonatomic, retain, readonly) id rootObject;
@property (nonatomic, retain, readonly) id rootObject;

/**
* Is this XML response an RSS feed? This distinction is necessary in order to allow duplicate
* keys in the XML objects.
*
* @default NO
*/
@property (nonatomic, assign) BOOL isRssFeed;

@end
14 changes: 13 additions & 1 deletion src/Three20/TTXMLParser.h
Expand Up @@ -27,10 +27,22 @@ extern NSString* kCommonXMLType_Unknown;
@private
id _rootObject;

BOOL _treatDuplicateKeysAsArrayItems;

NSMutableArray* _objectStack;
}

@property (nonatomic, readonly) id rootObject;
@property (nonatomic, readonly) id rootObject;

/**
* When a duplicate key is encountered, the key's value is turned into an array and both
* the original item and the new duplicate item are added to the array. Any subsequent duplicates
* are also added to this array.
* This is useful for RSS feeds, where feed items are presented inline (and not as array items).
*
* @default NO
*/
@property (nonatomic, assign) BOOL treatDuplicateKeysAsArrayItems;

@end

Expand Down

0 comments on commit 7183db6

Please sign in to comment.