iPhone: trouble parsing Google weather API - iphone

I'm downloading Google weather through the API. I'm having trouble parsing.
Using NSXMLParserDelegate, it finds the element "forecast_conditions," but I cannot seem to extract it's content. The parsers seems to think "high" and "low" etc are separate elements.
This is an element I'm try to parse:
<forecast_conditions><day_of_week data="Sun"/><low data="20"/><high data="38"/><icon data="/ig/images/weather/sunny.gif"/><condition data="Clear"/></forecast_conditions>
I'm surprised ...
`- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
NSLog(#"foundCharacters string: %#",string);
}`
doesn't return anything either. I thought this parsed the contents of an element
Help appreciated.

It turns out the parser creates the "attributeDict" dictionary automatically. Cycling through the elementNames, you only need to call [attributeDict objectForKey:#"data"]. Example below.
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
// current conditions
if ([elementName isEqualToString:#"current_conditions"]) {
currentConditions = [[NSMutableString alloc] init];
temp_f = [[NSMutableString alloc] init];
temp_c = [[NSMutableString alloc] init];
humidity = [[NSMutableString alloc] init];
currentIcon = [[NSMutableString alloc] init];
wind_condition = [[NSMutableString alloc] init];
}
if ([elementName isEqualToString:#"condition"]) {
[currentConditions appendString:[attributeDict objectForKey:#"data"]];
}
if ([elementName isEqualToString:#"temp_f"]) {
[temp_f appendString:[attributeDict objectForKey:#"data"]];
}
if ([elementName isEqualToString:#"temp_c"]) {
[temp_c appendString:[attributeDict objectForKey:#"data"]];
}
if ([elementName isEqualToString:#"humidity"]) {
[humidity appendString:[attributeDict objectForKey:#"data"]];
}
if ([elementName isEqualToString:#"currentIcon"]) {
[currentIcon appendString:[attributeDict objectForKey:#"data"]];
}
if ([elementName isEqualToString:#"wind_condition"]) {
[wind_condition appendString:[attributeDict objectForKey:#"data"]];
}

Related

How Do I Parse XML for Multiple Attributes?

heightI'm a noob to iphone development and I am trying to parse an xml with elements that contain multiple attributes. I have several tutorials on parsing XML, but none of them show the steps to parse an element with multiple attributes. I am trying to parse the xml with NSXMLParser. I want to parse the first occurrence of the media:thumbnail element for the value of the second attribute. Any help is greatly appreciated.
MY CODE:
//XML
<item>
<title>March 15 AM Metals Commentary: Eric Zuccarelli</title>
<link>http://link.brightcove.com/services/link/bcpid1683318714001/bclid1644543007001/bctid2228319176001?src=mrss</link>
<description>Eric Zuccarelli, Independent Copper Trader</description>
<guid>http://link.brightcove.com/services/link/bcpid1683318714001/bclid1644543007001/bctid2228319176001?src=mrss</guid>
<pubDate>Fri, 15 Mar 2013 06:40:38 -0700</pubDate>
<media:player height="546" url="http://link.brightcove.com/services/link/bcpid1683318714001/bclid1644543007001/bctid2228319176001?src=mrss" width="966" / >
<media:keywords>commentary,CME Group,financial products,cmedaily,nymex,metals,youtube,Market Commentary,Zuccarelli</media:keywords>
<media:thumbnail height="90" url="http://brightcove.vo.llnwd.net/d21/unsecured/media/49919183001/49919183001_2228333209001_th-514324d6e4b02e906f7476ba-806787304001.jpg?pubId=49919183001" width="120" / >
<media:thumbnail height="360" url="http://brightcove.vo.llnwd.net/d21/unsecured/media/49919183001/49919183001_2228333208001_vs-514324d6e4b02e906f7476ba-806787304001.jpg?pubId=49919183001" width="480" / >
<media:category>Metals</media:category>
<bc:playerid>1683318714001</bc:playerid>
<bc:lineupid>1644543007001</bc:lineupid>
<bc:titleid>2228319176001</bc:titleid>
<bc:duration>215</bc:duration>
<dcterms:valid / >
<bc:accountid>49919183001</bc:accountid>
</item>
My code is as follws:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
//NSLog(#"found this element: %#", elementName);
currentElement = [elementName copy];
if ([elementName isEqualToString:#"item"]) {
// clear out our story item caches...
item = [[NSMutableDictionary alloc] init];
currentTitle = [[NSMutableString alloc] init];
currentDate = [[NSMutableString alloc] init];
currentSummary = [[NSMutableString alloc] init];
currentLink = [[NSMutableString alloc] init];
currentBright = [[NSMutableString alloc] init];
if([elementName isEqualToString:#"media:thumbnail"]){
if([attributeDict objectForKey:#"height"]==90){
currentBright = [attributeDict objectForKey:#"url"];
NSLog(#"Brightcove Url:%#", currentBright); //<--Not reaching this point.
}
}
}
}
EDIT
if([elementName isEqualToString:#"media:thumbnail"]&&[attributeDict objectForKey:#"height"]==90){
currentBright = [attributeDict objectForKey:#"
NSLog(#"Brightcove Url:%#", currentBright);
}
Just tryout this....
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
currentElement = [elementName copy];
if ([elementName isEqualToString:#"item"]) {
item = [[NSMutableDictionary alloc] init];
currentTitle = [[NSMutableString alloc] init];
currentDate = [[NSMutableString alloc] init];
currentSummary = [[NSMutableString alloc] init];
currentLink = [[NSMutableString alloc] init];
currentBright = [[NSMutableString alloc] init];
}
else if([elementName isEqualToString:#"media:thumbnail"]) {
if([attributeDict objectForKey:#"height"]==90) {
currentBright = [attributeDict objectForKey:#"url"];
NSLog(#"Brightcove Url:%#", currentBright);
}
}
}
You're never going to hit your second or third conditional because how can elementName have changed without being advanced? Instead you should un-nest your conditionals.
Right now what happens is:
-didStartTag
ElementName = item -> first condtional hit -> fails second
-didStartTag
ElementName = title -> fails first conditonal
etc....

Leak occur at the time of Parsing

I'm actually doing parsing at the start of my program and this is my parser.m class i found
"leak" in this class in method "foundCharacters" in line
currentElementValue = [[NSMutableString alloc] initWithString:string];
currentElementValue is NSMutableString which is declare in parser.h file
NSMutableString *currentElementValue;
Can any one suggest me the way to solve this leak which some times crash my app in the starting...
Thanks In Advance...
didStartElement Method:-
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict {
if([elementName isEqualToString:#"posts"]) {
appDelegate.newsArray = [[NSMutableArray alloc] init];
}
else
{
if([elementName isEqualToString:#"page"])
{
aNewsInfo = [[NewsInfo alloc] init];
aNewsInfo.page = [[attributeDict objectForKey:#"id"] integerValue];
}
if(![elementName compare:#"smallimage"])
{
currentElementValue = [NSMutableString string];
}
if(![elementName compare:#"largeimage"])
{
currentElementValue = [NSMutableString string];
}
}
NSLog(#"Processing Element :%#",elementName);
}
Leak found in this foundCharacter method:- in line....
currentElementValue = [[NSMutableString alloc] initWithString:string];
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if(!currentElementValue)
currentElementValue = [[NSMutableString alloc] initWithString:string];
else
[currentElementValue appendString:string];
NSLog(#"Processing Value: %#", currentElementValue);
}
didEndElement method:-
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if([elementName isEqualToString:#"posts"])
return;
if([elementName isEqualToString:#"page"]) {
[appDelegate.newsArray addObject:aNewsInfo];
NSLog(#"%d",[appDelegate.newsArray count]);
NSLog(#"%#",aNewsInfo.title);
NSLog(#"%#",aNewsInfo.fulltext);
[aNewsInfo release];
aNewsInfo = nil;
}
else {
if ([elementName isEqualToString:#"smallimage"])
{
NSURL *url = [NSURL URLWithString:currentElementValue];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [[UIImage alloc] initWithData:data];
[aNewsInfo setSmallImageData:image];
}
if(![elementName compare:#"largeimage"])
{
NSURL *imageURL = [NSURL URLWithString:currentElementValue];
NSData *data = [NSData dataWithContentsOfURL:imageURL];
UIImage *image = [[UIImage alloc] initWithData:data];
[aNewsInfo setLargeImageData:image];
[image release];
}
[aNewsInfo setValue:currentElementValue forKey:elementName];
[currentElementValue release];
currentElementValue = nil;
}
}
First of all you need to do some study:
Go to following links:
http://www.cocoadev.com/index.pl?MemoryManagement
http://mattpatenaude.com/ch3-memory-management.html
http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/memorymgmt/Articles/MemoryMgmt.html
then do this,
currentElementValue = [[[NSMutableString alloc] initWithString:string] autorelease]
and remove [currentElementValue release], from
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
(otherwise it will leads to crash due to over releasing)

Parse 2 RSS feeds in app delegate

I'm following a tutorial and it's working fine but if I wanted to parse 2 RSS feeds it seems to overwrite one array instead of saving them in the respective arrays.
This is in my delegate:
NSURL *url2 = [[NSURL alloc] initWithString:#"myRSSFEED1"];
NSXMLParser *xmlParser1 = [[NSXMLParser alloc] initWithContentsOfURL:url2];
//Initialize the delegate.
XMLParser1 *parser1 = [[XMLParser1 alloc] initXMLParser];
//Set delegate
[xmlParser1 setDelegate:parser1];
//Start parsing the XML file.
BOOL successs = [xmlParser1 parse];
if(successs)
NSLog(#"No Errors");
else
NSLog(#"Error Error Error!!!");
//VIDS
NSURL *url = [[NSURL alloc] initWithString:#"MYRSSFEED2"];
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
//Initialize the delegate.
XMLParser *parser = [[XMLParser alloc] initXMLParser];
//Set delegate
[xmlParser setDelegate:parser];
//Start parsing the XML file.
BOOL success = [xmlParser parse];
if(success)
NSLog(#"No Errors");
else
NSLog(#"Error Error Error!!!");
that parses feed 1, allocates it to the array then parses 2 and seems to overwrite the first insteaf of using the second array that are defined as
#synthesize pics;
#synthesize books;
And saved in my XMLParser & XMLParser1
I can't figure out how to stop it from overwriting.
Here is my XMLParsers too:
- (void)parsers:(NSXMLParser *)parsers didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict {
if([elementName isEqualToString:#"Books"]) {
//Initialize the array.
appDelegate2.pics = [[NSMutableArray alloc] init];
}
else if([elementName isEqualToString:#"Book"]) {
//Initialize the book.
apics = [[BookPhoto alloc] init];
//Extract the attribute here.
apics.bookID = [[attributeDict objectForKey:#"id"] integerValue];
NSLog(#"Reading HAVid value :%i", apics.bookID);
}
NSLog(#"Processing Element: %#", elementName);
}
- (void)parsers:(NSXMLParser *)parsers foundCharacters:(NSString *)string {
if(!currentElementValue)
currentElementValue = [[NSMutableString alloc] initWithString:string];
else
[currentElementValue appendString:string];
NSLog(#"Processing Value: %#", currentElementValue);
}
- (void)parsers:(NSXMLParser *)parsers didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if([elementName isEqualToString:#"Books"])
return;
//There is nothing to do if we encounter the Books element here.
//If we encounter the Book element howevere, we want to add the book object to the array
// and release the object.
if([elementName isEqualToString:#"Book"]) {
[appDelegate2.pics addObject:apics];
[apics release];
apics = nil;
}
else
[apics setValue:currentElementValue forKey:elementName];
[currentElementValue release];
currentElementValue = nil;
}
And my XMLParser.m
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict {
if([elementName isEqualToString:#"Books"]) {
//Initialize the array.
appDelegate.books = [[NSMutableArray alloc] init];
}
else if([elementName isEqualToString:#"Book"]) {
//Initialize the book.
aBook = [[Book alloc] init];
//Extract the attribute here.
aBook.bookID = [[attributeDict objectForKey:#"id"] integerValue];
NSLog(#"Reading id value :%i", aBook.bookID);
}
NSLog(#"Processing Element: %#", elementName);
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if(!currentElementValue)
currentElementValue = [[NSMutableString alloc] initWithString:string];
else
[currentElementValue appendString:string];
NSLog(#"Processing Value: %#", currentElementValue);
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if([elementName isEqualToString:#"Books"])
return;
//There is nothing to do if we encounter the Books element here.
//If we encounter the Book element howevere, we want to add the book object to the array
// and release the object.
if([elementName isEqualToString:#"Book"]) {
[appDelegate.books addObject:aBook];
[aBook release];
aBook = nil;
}
else
[aBook setValue:currentElementValue forKey:elementName];
[currentElementValue release];
currentElementValue = nil;
}
Any help on this would be brilliant,
I suggest putting a breakpoint at the point where the array is not getting loaded, and seeing if (1) it even gets called, and (2), whether it is saving the data into the array at all.
my bad
seems i had extra s's in parser:
- (void)parsers:(NSXMLParser *)parsers foundCharacters:(NSString *)string {
if(!currentElementValue)
currentElementValue = [[NSMutableString alloc] initWithString:string];
else
[currentElementValue appendString:string];
NSLog(#"Processing Value: %#", currentElementValue);
}
should have been:
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if(!currentElementValue)
currentElementValue = [[NSMutableString alloc] initWithString:string];
else
[currentElementValue appendString:string];
NSLog(#"Processing Value: %#", currentElementValue);
}
cheers for the help tho

iPhone XML Parsing problem

I am trying to extract the title from this xml:
<entry>
...
<title type="html">Some title</title>
<published>2011-02-07T00:04:16Z</published>
<updated>2011-02-07T00:04:16Z</updated>
...
</entry>
in the NSXMLParsing's didStartElement code:
if ([elementName isEqualToString:#"entry"])
{
item = [[NSMutableDictionary alloc] init];
}
if([elementName isEqualToString:#"title"])
{
currentElement = [elementName copy];
currentTitle = [[NSMutableString alloc] init];
}
in foundCharacters:
if ([currentElement isEqualToString:#"title"])
{
[currentTitle appendString:string];
}
in didEndElement:
if ([elementName isEqualToString:#"entry"])
{
[item setObject:currentTitle forKey:#"title"];
[currentElement release];
currentElement = nil;
[item release];
item = nil;
}
The problem is that for some reason when it gets to the didEndElement the currentTitle has got the three node's content, so its:
Some
title2011-02-07T00:04:16Z2011-02-07T00:04:16Z
I don't get why its picking up the published and updated node and appending them to the title string.
You set currentElement to #"title" in didStartElement: but you never unset it. The first element in this particular XML file is title, so you set currentElement and for every foundCharacters: thereafter you append them. Change didStartElement: to:
currentElement = [elementName copy];
if ([elementName isEqualToString:#"entry"])
{
item = [[NSMutableDictionary alloc] init];
}
if([elementName isEqualToString:#"title"])
{
currentTitle = [[NSMutableString alloc] init];
}
You have to add to didEndElement:
if ([elementName isEqualToString:#"title"])
{
[currentElement release];
currentElement = nil;
}
define
NSMutableString *strFoundCharacters; in class method set property and synthesize it.
then in
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *) string {
NSString *strAppend = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ([strAppend length] > 0) {
[self.strFoundCharacters appendString:string];
}
}
make nil to strfoundcharacter in
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if ([elementName isEqualToString:#"title"] ) {
self.currentTitle = self.strFoundCharacters;
}
[self.strFoundCharacters setString:#""];
}
i think it will work

Data Types and XMLParser

I syncing the data on my website to my app, I'm using NSXMLParser to do this. The problem is I have all the fields on my database defined as Strings. The sync process works fine when everything is a string, but this is causing me heartache further down the line when I try and use this data for other purposes.
Can anyone help me with defining my fields with the correct data types for the sync process, code below:
.m
// Array for WORKOUT.
NSMutableString *currentID, *currentUserID, *currentWalkID, *currentDate, *currentDistance, *currentRepeats, *currentType, *currentIntensity,
*currentComments, *currentTime, *currentWeight, *currentHeight;
I know its something to do with this NSMutableString, obviously everything is defined as a string.
.h
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
currentElement = [elementName copy];
// Check for the WORKOUT details in the XML feed.
if ([elementName isEqualToString:#"workout"])
{
// clear out our workout item caches...
item = [[NSMutableDictionary alloc] init];
currentID = [[NSMutableString alloc] init];
currentUserID = [[NSMutableString alloc] init];
currentWalkID = [[NSMutableString alloc] init];
currentDate = [[NSMutableString alloc] init];
currentDistance = [[NSMutableString alloc] init];
currentRepeats = [[NSMutableString alloc] init];
currentType = [[NSMutableString alloc] init];
currentIntensity = [[NSMutableString alloc] init];
currentComments = [[NSMutableString alloc] init];
currentTime = [[NSMutableString alloc] init];
currentWeight = [[NSMutableString alloc] init];
currentHeight = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:#"workout"])
{
Workout *newWorkout = [NSEntityDescription insertNewObjectForEntityForName:#"Workout" inManagedObjectContext: self.managedObjectContext];
// save values to an item, then store that item into the array...
[item setObject:currentID forKey:#"workout_id"];
[item setObject:currentUserID forKey:#"workout_user_id"];
[item setObject:currentWalkID forKey:#"workout_walk_id"];
[item setObject:currentDate forKey:#"workout_date"];
[item setObject:currentDistance forKey:#"workout_distance"];
[item setObject:currentRepeats forKey:#"workout_repeats"];
[item setObject:currentType forKey:#"workout_type"];
[item setObject:currentIntensity forKey:#"workout_intensity"];
[item setObject:currentComments forKey:#"workout_comments"];
[item setObject:currentTime forKey:#"workout_time"];
[item setObject:currentWeight forKey:#"workout_weight"];
[item setObject:currentHeight forKey:#"workout_height"];
newWorkout.workout_id = currentID;
newWorkout.workout_user_id = currentUserID;
newWorkout.workout_walk_id = currentWalkID;
newWorkout.workout_date = currentDate;
newWorkout.workout_distance = currentDistance;
newWorkout.workout_repeats = currentRepeats;
newWorkout.workout_type = currentType;
newWorkout.workout_intensity = currentIntensity;
newWorkout.workout_comments = currentComments;
newWorkout.workout_time = currentTime;
newWorkout.workout_weight = currentWeight;
newWorkout.workout_height = currentHeight;
[self.workoutArray addObject:newWorkout];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
// save the characters for the current item...
if ([currentElement isEqualToString:#"workout_id"]) {
[currentID appendString:string];
} else if ([currentElement isEqualToString:#"workout_user_id"]) {
[currentUserID appendString:string];
} else if ([currentElement isEqualToString:#"workout_walk_id"]) {
[currentWalkID appendString:string];
} else if ([currentElement isEqualToString:#"workout_date"]) {
[currentDate appendString:string];
} else if ([currentElement isEqualToString:#"workout_distance"]) {
[currentDistance appendString:string];
} else if ([currentElement isEqualToString:#"workout_repeats"]) {
[currentRepeats appendString:string];
} else if ([currentElement isEqualToString:#"workout_type"]) {
[currentType appendString:string];
} else if ([currentElement isEqualToString:#"workout_intensity"]) {
[currentIntensity appendString:string];
} else if ([currentElement isEqualToString:#"workout_comments"]) {
[currentComments appendString:string];
} else if ([currentElement isEqualToString:#"workout_time"]) {
[currentTime appendString:string];
} else if ([currentElement isEqualToString:#"workout_weight"]) {
[currentWeight appendString:string];
} else if ([currentElement isEqualToString:#"workout_height"]) {
[currentHeight appendString:string];
}
XML can only carry strings. Wheather it's an element's inner text or the value of an attribute, in the end everything is just a string.
If you want to send anything other via XML, the sending side has to encode it in a string and the receiving side has to decode the string. For that to work both sides have to agree on a format, for Example for an Integer value one possibility is to encode 125 as "125", or a float 2.5 as "2.5". If you are using this format for your numbers you can decode them by using the integerValue and floatValue of NSString.
float test = [#"2.5" floatValue];
There are some standard formats defined in xsd, that you could use, but that will not help you decode them, if you don't have a parser that does this for you. (NSXMLParser is no such)
If you are using other Formats look at NSScanner. For dates and time you would like to look at NSDateFormatter.
If you need help converting please post the format you are using, that is an excerpt of your XML.