I'd use a NSMutableArray as stack:
As soon as a parent tag
is found, I create a parent
object, and put it on that stack.
As soon as a child
tag is found, I create a child objects and put it on a stack. Now when ever a tag is found, that should be saved as property on an objects, I just add it to the last object.
If I have a child on top of the stack, I know it's parent is the object below it. Now I can establish any form of relationship.
If a closing tag of a parent of child is found, I remove the last object from the stack.
a minimal example:
We want to parse
<root>
<parent>
<title>Parent1</title>
<child>
<title>Child 1</title>
</child>
<child>
<title>Child 2</title>
</child>
</parent>
<parent>
<title>Parent 2</title>
<child>
<title>Child 3</title>
</child>
<child>
<title>Child 3</title>
</child>
</parent>
</root>
Our data structure:
@interface TitledObject : NSObject
@property (nonatomic, copy) NSString *title;
@end
@implementation TitledObject
-(NSString *)description
{
return [NSString stringWithFormat:@"%@: %@", NSStringFromClass([self class]), _title];
}
@end
@interface Parent :TitledObject
@property (nonatomic, strong) NSMutableArray *children;
@end
@implementation Parent
@end
@interface Child : TitledObject
@property (nonatomic, weak) Parent *parent;
@end
@implementation Child;
-(NSString *)description
{
NSString *s = [super description];
s = [s stringByAppendingString:[NSString stringWithFormat:@" , child of %@", self.parent]];
return s;
}
@end
And the parser:
@interface XMLReader : NSObject <NSXMLParserDelegate>
@property (nonatomic, strong) NSString *string;
@property (nonatomic, strong) NSMutableArray *stack;
@property (nonatomic, strong) NSMutableArray *results;
-(id) initWithString:(NSString *)string;
@end
@implementation XMLReader{
NSMutableString *title;
}
-(id)initWithString:(NSString *)string
{
if(self = [super init]){
_string = string;
_stack = [NSMutableArray array];
_results = [NSMutableArray array];
}
return self;
}
-(void)parse
{
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:[self.string dataUsingEncoding:NSUTF8StringEncoding]];
parser.delegate = self;
[parser parse];
}
-(void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqualToString:@"parent"]) {
[self.stack addObject:[[Parent alloc] init]];
} else if ([elementName isEqualToString:@"child"]) {
Child *child = [[Child alloc] init];
child.parent = [self.stack lastObject];
[child.parent.children addObject:child];
[self.stack addObject:child];
} else if ([elementName isEqualToString:@"title"]) {
title = [@"" mutableCopy];
}
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:@"title"])
[(TitledObject *)[self.stack lastObject] setTitle:title];
else if (! [elementName isEqualToString:@"root"]){
[self.results addObject:[self.stack lastObject]];
[self.stack removeLastObject];
}
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
[title appendString:string];
}
@end
we use it like:
XMLReader *reader = [[XMLReader alloc] initWithString:xmlString];
[reader parse];
and the access the results:
NSLog(@"%@", reader.results);
Output:
(
"Child: Child 1 , child of Parent: Parent1",
"Child: Child 2 , child of Parent: Parent1",
"Parent: Parent1",
"Child: Child 3 , child of Parent: Parent 2",
"Child: Child 3 , child of Parent: Parent 2",
"Parent: Parent 2"
)
Instead of my simple data structure you can use managed objects.