There are a couple of problems:
The
didEndElement
fora-location
sets the value keyed by the date, but then takes the pointer that points to that same object and removes all the items that were in that object. So, instead of:if ( [elementName isEqualToString:@"a-location"]) { [parsedNearMe setObject:tempArray forKey:[NSDate date]]; [tempArray removeAllObjects]; }
You might replace that with:
if ( [elementName isEqualToString:@"a-location"]) { [parsedNearMe setObject:tempArray forKey:[NSDate date]]; tempArray = nil; // make sure you don't touch that object you just added to parsedNearMe }
I doubt this particular XML will suffer from this problem, but your
foundCharacters
assumes that a single call will return all of the data. That's not a valid assumption. Sometimes it takes multiple calls.You're also having
foundCharacters
append strings for portions of the XML for which you have no interest. You should make sure thatcurrentString
isnil
except when parsing latitude, longitude, and name.
So, at the very least, I'd suggest something like the following:
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
parsedNearMe = [[NSMutableDictionary alloc] init];
currentString = nil;
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ( [elementName isEqualToString:@"a-location"])
{
tempArray = [[NSMutableArray alloc] init];
}
else if ([elementName isEqualToString:@"latitude"] ||
[elementName isEqualToString:@"longitude"] ||
[elementName isEqualToString:@"the-name"])
{
currentString = [[NSMutableString alloc] init];
}
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
[currentString appendString:string];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:@"latitude"] ||
[elementName isEqualToString:@"longitude"] ||
[elementName isEqualToString:@"the-name"] )
{
[tempArray addObject:currentString];
currentString = nil;
}
else if ( [elementName isEqualToString:@"a-location"])
{
[parsedNearMe setObject:tempArray forKey:[NSDate date]];
tempArray = nil;
}
}
This mirrors your current logic, creating dictionary keyed by some random timestamp, and the value for each dictionary item is an array of latitude, longitude, and name.
Personally, I think this design suffers from two additional flaws, though:
You're keying your dictionary by some random object,
[NSDate date]
. That offers no value. You might as well make this an array.Your array of latitude, longitude, and the-name is ambiguous. Your data structure is highly contingent upon the order of items in the XML file. If you're going to use a dictionary for anything, I'd use it here.
So, bottom line, rather than a dictionary whose values are arrays, I'd flip that around. I'd suggest an array of dictionary objects. I think the parsedNearMe
should be a NSMutableArray
. And instead of a NSMutableArray
called tempArray
, you should use a NSMutableDictionary
called tempDictionary
:
First, define a few ivars:
NSMutableArray *parsedNearMe; // this was a NSMutableDictionary
NSMutableDictionary *tempDictionary;
And then your NSXMLParserDelegate
routines might look like:
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
parsedNearMe = [[NSMutableArray alloc] init];
currentString = nil;
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ( [elementName isEqualToString:@"a-location"])
{
tempDictionary = [[NSMutableDictionary alloc] init];
}
else if ([elementName isEqualToString:@"latitude"] ||
[elementName isEqualToString:@"longitude"] ||
[elementName isEqualToString:@"the-name"])
{
currentString = [[NSMutableString alloc] init];
}
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
[currentString appendString:string];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:@"latitude"] ||
[elementName isEqualToString:@"longitude"] ||
[elementName isEqualToString:@"the-name"] )
{
[tempDictionary setObject:currentString forKey:elementName];
currentString = nil;
}
else if ([elementName isEqualToString:@"a-location"])
{
[parsedNearMe addObject:tempDictionary];
tempDictionary = nil;
}
}
Or, if you'd like to store latitude and longitude as NSNumber
objects instead as strings, you could replace the didEndElement
with:
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:@"latitude"] ||
[elementName isEqualToString:@"longitude"])
{
[tempDictionary setObject:@([currentString doubleValue]) forKey:elementName];
currentString = nil;
}
else if ([elementName isEqualToString:@"the-name"] )
{
[tempDictionary setObject:currentString forKey:elementName];
currentString = nil;
}
else if ([elementName isEqualToString:@"a-location"])
{
[parsedNearMe addObject:tempDictionary];
tempDictionary = nil;
}
}