Question

I was having issues parsing XML, so I went through my code with NSLogs and noticed - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string was not called.

My XML:

<?xml version="1.0"?>
<locations>
<location>
<name>Americas</name>
<address></address>
<gid>CoQBdAAAAE2tdzw4RGvnvaUZTk6N1ByNtosmD--ZYfcmkkPkR6R6v_nN9gJCO1INOVg-S5rzy7BATEUmvAQzh8hClafZbph2wSgfD28gXNJAttXLUbqzQMaql3rVbisSUv2a2r_H6ktOSaI5lLvf0GNthT5jn2JfAdD_HfUls6qhMvrm5e5XEhDcF5uRjlfpSqI_aciS_QPaGhQVVHenbOKZBQMSoCzsoIsdfw313Q</gid>
<type>
<1>political</1><2/><3/><4/><5/>
</type>
<phone></phone>
<rating></rating>
<icon>http://maps.gstatic.com/mapfiles/place_api/icons/geocode-71.png</icon>
<website></website>
<longitude>-105.2551187</longitude>
<latitude>54.5259614</latitude>
</location>
</locations>

Relevant Part of my Implementation

- (void)viewDidLoad
{
    [super viewDidLoad];
...
    if(!self.distance){self.distance = @"250000";}
    NSString *string = [NSString stringWithFormat:@"http://fishbe.in/lab/reserverr/api/?term=%@&lat=%f&long=%f&limit=100&distance=%@", self.searchterm, location.coordinate.latitude, location.coordinate.longitude, self.distance];
    NSLog(@"%@", string);
    NSURL *URL = [NSURL URLWithString:string];
    NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:URL];

    [parser setDelegate:self];
    [parser setShouldResolveExternalEntities:NO];
    [parser setShouldProcessNamespaces:NO];
    [parser setShouldReportNamespacePrefixes:NO];
    [parser parse];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
    self.element = elementName;
    if ([self.element isEqualToString:@"locations"]) {
        self.items    = [[NSMutableArray alloc] init];
    }

}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
    NSLog(@"HI"); //Not outputted
    if ([self.element isEqualToString:@"name"]) {
        self.result = [[ReserverrSearchResult alloc] init];
        [self.result setTitle:string];
        NSLog(@"%@", string);
    } else if ([self.element isEqualToString:@"address"]) {
        [self.result setVicinity:string];
        NSLog(@"%@", string);
    } else if ([self.element isEqualToString:@"gid"]) {
        [self.result setGid:string];
        NSLog(@"%@", string);
    } else if ([self.element isEqualToString:@"latitude"]) {
        [self.result setLatitude:[string floatValue]];
        NSLog(@"%@", string);
    } else if ([self.element isEqualToString:@"longitude"]) {
        [self.result setLongitude:[string floatValue]];
        NSLog(@"%@", string);
    }

}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
    if ([elementName isEqualToString:@"location"]) {
        [self.items addObject:self.result];

    }

}

- (void)parserDidEndDocument:(NSXMLParser *)parser {

    [self addAnnotations];

}

Edit - Added

- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
    NSLog(@"%@",parseError);
}

and got nothing.

Was it helpful?

Solution

There are a couple of issues:

  1. As wain points out, you should implement parser:parseErrorOccurred: and it will tell you what's wrong.

  2. I'd expect problems if the searchterm contains any characters that are reserved characters for a URL (e.g. a space, a plus sign, an ampersand, etc.). You should always percent-escape what I presume is a user provided search term and adding it to a URL:

    NSString *string = [NSString stringWithFormat:@"http://fishbe.in/lab/reserverr/api/?term=%@&lat=%f&long=%f&limit=100&distance=%@", [self percentEscapeString:self.searchterm], location.coordinate.latitude, location.coordinate.longitude, self.distance];
    

    where you can use a method like the following:

    - (NSString *)percentEscapeString:(NSString *)string
    { 
        NSString *result = CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
                                                                                     (CFStringRef)string,
                                                                                     (CFStringRef)@" ",
                                                                                     (CFStringRef)@":/?@!$&'()*+,;=",
                                                                                     kCFStringEncodingUTF8));
        return [result stringByReplacingOccurrencesOfString:@" " withString:@"+"];
    }
    

    Note, do not rely upon stringByAddingPercentEscapesUsingEncoding to do the percent-escaping. You really want to use CFURLCreateStringByAddingPercentEscapes, like above.

  3. As an aside, your XML is not well-formed. I don't believe that NSXMLParser will accept element names that are numbers. So, instead of:

    <type>
        <1>political</1><2/><3/><4/><5/>
    </type>
    

    You might want:

    <types>
        <type>political</type>
        <type></type>
        <type></type>
        <type></type>
        <type></type>
    </types>
    

    Or, if you really need those numeric identifiers:

    <types>
        <type id="1">political</type>
        <type id="2"></type>
        <type id="3"></type>
        <type id="4"></type>
        <type id="5"></type>
    </types>
    

    In this latter example, your didStartElement can then use the attributeDict to extract the id identifier.

  4. You report that parseErrorOccurred did not return anything. That means your server probably didn't return anything. And this could be caused by any of a number of problems. Try using your URL and return the data (for example, into a NSString or NSData, rather than the parser) and see what the response looked like. E.g., for testing purposes you could do:

    NSError  *error;
    NSData   *data    = [NSData dataWithContentsOfURL:url options:0 error:&error];
    NSString *results = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    
    // confirm error is `nil` and `results` is well-formed XML;
    // you can then pass the `data` to `NSXMLParser` with `initWithData` if you want
    

    I bet it's empty (which for certain search terms could be caused by a lack of percent-escaping, or from any of a myriad of server problems).

  5. It might not be a problem, but it's worth noting that it's conceptually risky to save the results in foundCharacters, because sometimes between didStartElement and didEndElement, you'll have multiple calls to foundCharacters to retrieve the data. Generally people will instantiate a NSMutableString in didStartElement, append to it in foundCharacters, and then save it in didEndElement.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top