xcode 4.2 Loop Through XML Data Elements - iphone

I am very confused on how I am supposed to loop through XML elements for my current iPhone app project. I will show my code, then clarify my question.
Here is sample XML I am using:
<restaurant_details>
<total_results>90</total_results>
<restaurant>
<name>Through the Garden</name>
<distance_from_current_location>0.55</distance_from_current_location>
<restaurant_id>123</restaurant_id>
<longitude>-84.373734</longitude>
<latitude>39.258606</latitude>
<address>10738 Kenwood Rd | cincinnati ,OH | 45242</address>
<phone_number>513-791-2199</phone_number>
<restaurant_type>General</restaurant_type>
</restaurant>
</restaurant_details>
Getting data for my table:
- (void)get_table_data {
NSString *day = [self dateInFormat:#"%A"];
NSLog (#"At get_table_data");
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.url.com"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
NSLog (#"At connection");
receivedData = [NSMutableData data];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
// NSString *theXML = [[NSString alloc] initWithBytes: [myWebData mutableBytes] length:[myWebData length] encoding:NSUTF8StringEncoding];
// NSLog(#"%#",theXML);[theXML release];
if(parser){
parser = nil;
}
parser = [[NSXMLParser alloc] initWithData: receivedData];
[parser setDelegate: self];
[parser setShouldResolveExternalEntities: YES];
[parser parse];
}
And my parser code:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict{
NSLog (#"At parser");
currentElement = elementName;
if ([currentElement isEqualToString:#"restaurant_details"]) {
if ([currentElement isEqualToString:#"total_results"]) {
//NSLog(#"Element: %#", currentElement);
}else if ([currentElement isEqualToString:#"restaurant"]) {
restaurantObj = [[DOR_RestaurantClass alloc]init];
}
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
if([currentElement isEqualToString:#"name"]) {
restaurantObj.name=[NSString stringWithFormat:#"%#",string];
//NSLog(#"Name to be saved in Array :- %#",string);
}
if([currentElement isEqualToString:#"distance_from_current_location"]) {
restaurantObj.distance=[NSString stringWithFormat:#"%#",string];
//NSLog(#"Name to be saved in Array :- %#",string);
}
if([currentElement isEqualToString:#"restaurant_id"]) {
restaurantObj.restId=[NSString stringWithFormat:#"%#",string];
//NSLog(#"Name to be saved in Array :- %#",string);
}
if([currentElement isEqualToString:#"address"]) {
NSArray *stringArray = [string componentsSeparatedByString:#" | "];
restaurantObj.address=[stringArray objectAtIndex:0];
restaurantObj.address2=[stringArray objectAtIndex:1];
restaurantObj.address3=[stringArray objectAtIndex:2];
//NSLog(#"Name to be saved in Array :- %#",string);
}
if([currentElement isEqualToString:#"phone_number"]) {
restaurantObj.phone=[NSString stringWithFormat:#"%#",string];
//NSLog(#"Name to be saved in Array :- %#",string);
}
if([currentElement isEqualToString:#"description"]) {
restaurantObj.description=[NSString stringWithFormat:#"%#",string];
//NSLog(#"Name to be saved in Array :- %#",string);
}
if([currentElement isEqualToString:#"image_url"]) {
restaurantObj.image=[NSString stringWithFormat:#"%#",string];
//NSLog(#"Name to be saved in Array :- %#",string);
}
if([currentElement isEqualToString:#"restaurant_type"]) {
//restaurantObj.Name=[NSString stringWithFormat:#"%#",string];
//NSLog(#"Name to be saved in Array :- %#",string);
}
NSLog (#"At parser2");
if(!currentElementValue)
currentElementValue=[[NSMutableString alloc] initWithString:string];
else
[currentElementValue appendString:string];
NSLog(#"the parser just found this text in a tag:%#",string);
}
-(void)parser:(NSXMLParser*)parser didEndElement:(NSString*)elementName namespaceURI:(NSString*)namespaceURI qualifiedName:(NSString*)qualifiedName {
NSLog(#"Current element in End Element Category:- %#",currentElement);
if([elementName isEqualToString:#"restaurant"]) {
[listItems addObject:restaurantObj];
NSLog(#"Array: %#", listItems);
}else{
currentElementValue=nil;
}
}
Here's my .h for my restaurant class:
#import <Foundation/Foundation.h>
#interface DOR_RestaurantClass : NSObject
#property (nonatomic, retain) NSString *restId;
#property (nonatomic, retain) NSString *image;
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *description;
#property (nonatomic, retain) NSString *distance;
#property (nonatomic, retain) NSString *address;
#property (nonatomic, retain) NSString *address2;
#property (nonatomic, retain) NSString *address3;
#property (nonatomic, retain) NSString *phone;
#property (nonatomic, retain) NSString *expires;
#end
And my .m:
#import "DOR_RestaurantClass.h"
#implementation DOR_RestaurantClass
#synthesize restId;
#synthesize image;
#synthesize name;
#synthesize description;
#synthesize distance;
#synthesize address;
#synthesize address2;
#synthesize address3;
#synthesize phone;
#synthesize expires;
#end
I am getting my data and so forth, so that's not an issue. My question is this: With multiple "restaurant" tags in my XML data, how do I sort through the XML so that I keep restaurant data together? I come from PHP, so I would have done a foreach on the restaurant tags, but I'm not sure how the NSXMLParser is supposed to keep the data seperated. I will be putting this information into an NSMutableArray in the end. I just don't know where to start, and cannot find any good examples on doing this. Any and all help is greatly appreciated.
This has been updated
My array listItems (NSMutableArray) is null when I print it to NSLog.

Firstly Create a Obj c class having .h and .m files only. and in that create some NSString Variables having properties like name, distance, longitude, latitude.. etc etc.
now, the code part :-
In didStartElement
in restaurant tag create a Object of that class which i just told u to create(initialize the Object):-
currentElement = elementName;
if([currentElement isEqualToString:#"restaurant"]) {
classObj = [[MyRestaurentClass alloc]init];
}
Now In foundCharacters find the other tags like this :-
if([currentElement isEqualToString:#"name"]) {
classObj.Name=[NSString stringWithFormat:#"%#",string];
NSLog(#"Name to be saved in Array :- %#",string);
}
if ( [currentElement isEqualToString:#"longitude"])
{
classObj.longitude =[NSString stringWithFormat:#"%#",string];
}
}
Now Last part in didEneElement :-
Create a MutubaleArray in Parser Class and Add the classObj in that array in didEndElement :-
if([elementName isEqualToString:#"restaurent"]) {
NSLog(#"Current element in End Element Category:- %#",currentElement);
[ObjectsMutableArray addObject:classObj];
}
And that's all. u r finished with parsing ..
UPDATE
Firstly you dont have to write like this in If loop i.e. :-
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict{
NSLog (#"At parser");
currentElement = elementName;
if ([currentElement isEqualToString:#"restaurant_details"]) {
if ([currentElement isEqualToString:#"total_results"]) {
//NSLog(#"Element: %#", currentElement);
}else if ([currentElement isEqualToString:#"restaurant"]) {
restaurantObj = [[DOR_RestaurantClass alloc]init];
}
}
}
What i wrote in answer for didStartElement is Sufficient as didStartElement is called for Each n Every Tag.
See Once u create a class having NSStrings acc to your XML. Just create an Object of that class and Initialize it in didStartElement.
in foundCharacters you will add the Required data in the classObject variables according to the If Conditions loops. Whenever the XMlParser comes in the </restaurent> Tag it will go in didEndElement method and there you just have to Put that classObject (object) in that Array like i did in didEndElement.
Ok Regarding your NSMutableArray, have you Initialized the Array in didStartDocument like this :-
(void)parserDidStartDocument:(NSXMLParser *)parser{
listItems = [[NSMutableArray alloc]init];
}
I have Changed a Above Code so please read again the whole answer carefully.
If u still have any doubts Text me...

Related

XMLParser Advice

I'm not sure what I am doing wrong. I have a URL leading to an XML tree that looks like:
<result>
...
<title>
...
</title>
<body>
...
</body>
...
</result>
I just need to parse the file and get the title and body. Here is my object.h:
#import <Foundation/Foundation.h>
#interface Object : NSObject
{
NSString *title;
NSString *description;
}
#property (nonatomic, retain) NSString *title;
#property (nonatomic, retain) NSString *description;
#end
And here is Object.m:
#import "Object.h"
#implementation Object
#synthesize title, description;
-(void) dealloc
{
[title release];
[description release];
[super dealloc];
}
#end
Here is my XMLParser.h:
#import <Foundation/Foundation.h>
#import "Object.h"
#interface XMLParser : NSObject <NSXMLParserDelegate>
{
NSMutableString *currentNodeContent;
NSMutableArray *arrayOfObjects;
NSXMLParser *parser;
Object *currentObject;
}
#property (readonly, retain) NSMutableArray *arrayOfObjects;
-(id) loadXMLbyURL:(NSString *)urlString;
#end
And finally, XMLParser.m:
#import "XMLParser.h"
#import "Object.h"
#implementation XMLParser
#synthesize arrayOfObjects;
-(id) loadXMLbyURL:(NSString *)urlString
{
arrayOfObjects = [[NSMutableArray alloc] init];
NSURL *url = [NSURL URLWithString:urlString];
NSData *data = [[[NSData alloc] initWithContentsOfURL:url] autorelease];
parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
[parser parse];
return self;
}
-(void) dealloc
{
[parser release];
[super dealloc];
}
-(void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if([elementName isEqualToString:#"result"])
{
currentObject = [Object alloc];
}
}
-(void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if([elementName isEqualToString:#"title"])
{
currentObject.title = currentNodeContent;
}
if([elementName isEqualToString:#"body"])
{
currentObject.description = currentNodeContent;
}
if([elementName isEqualToString:#"result"])
{
[arrayOfObjects addObject:currentObject];
[currentObject release];
currentObject = nil;
[currentNodeContent release];
currentNodeContent = nil;
}
}
-(void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
currentNodeContent = (NSMutableString *) [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
#end
In my main code I make a call to XML parser like this:
xmlParser = [[XMLParser alloc] loadXMLbyURL:#"www.websiteWithXML.com"];
However nothing is being placed into my arrayOfObjects (I know this because when I tell a tableview to have as many rows as my array, there are no rows).
please help!!! Thank you in advance!
Not sure if this will help...
Try changing:
arrayOfObjects = [[NSMutableArray alloc] init];
To:
arrayOfObjects = [[NSMutableArray alloc] initWithCapacity:0];
Other than that it appears that your code is fine. Also just because your table isn't loading anything doesn't mean that you do not have objects in your array. Use breakpoints to take a look at your data after you finish parsing and before you try to load your tableview.
Try resetting currentNodeContent inside each of your element starts. For example:
-(void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if([elementName isEqualToString:#"result"])
{
currentObject = [Object alloc];
}
else if ([elementName isEqualToString:#"title"] || [elementName isEqualToString:#"body"])
{
[currentNodeContent setString:#""];
}
}
Then, when you receive characters do an append:
-(void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
[currentNodeContent appendString:[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
}
By doing this, you handle the situation where a foundCharacters gets multiple times to capture all of the characters within a given element.

NSXMLParser multiple call - BAD ACCESS

hello i want to parse html an with this information another html file...
after 1-5 call the program crashes...
header:
#import <UIKit/UIKit.h>
#interface FirstViewController : UIViewController <ZBarReaderDelegate, NSXMLParserDelegate>{
UIImageView *resultImage;
UITextView *resultText;
NSString *product_link;
NSXMLParser *parseHTML;
NSXMLParser *parseHTML2;
NSMutableArray *myMutableArray;
id <NSXMLParserDelegate> testkollege, asdf;
}
#property (nonatomic, retain) IBOutlet UIImageView *resultImage;
#property (nonatomic, retain) IBOutlet UITextView *resultText;
#property (nonatomic, assign) IBOutlet NSString *product_link;
#property (nonatomic, assign) NSXMLParser *parseHTML;
#property (nonatomic, assign) NSXMLParser *parseHTML2;
#property (nonatomic, retain) NSMutableArray *myMutableArray;
#property (nonatomic, assign) id <NSXMLParserDelegate> testkollege;
#property (nonatomic, assign) id <NSXMLParserDelegate> asdf;
- (IBAction) scanButtonTapped;
#end
m-file:
#import "FirstViewController.h"
#import "/System/Library/Frameworks/Foundation.framework/Headers/NSDebug.h"
#implementation FirstViewController
#synthesize resultImage, resultText;
#synthesize product_link;
#synthesize parseHTML, parseHTML2;
#synthesize myMutableArray;
#synthesize testkollege, asdf;
bool link_is_here = false;
bool allergy_is_here = false;
bool parse_one_ok = true;
- (void) imagePickerController: (UIImagePickerController*) reader
didFinishPickingMediaWithInfo: (NSDictionary*) info
{
// ADD: get the decode results
id<NSFastEnumeration> results = [info objectForKey: ZBarReaderControllerResults];
ZBarSymbol *symbol = nil;
for(symbol in results)
// EXAMPLE: just grab the first barcode
break;
// EXAMPLE: do something useful with the barcode data
resultText.text = symbol.data;
// EXAMPLE: do something useful with the barcode image
resultImage.image =
[info objectForKey: UIImagePickerControllerOriginalImage];
// ADD: dismiss the controller (NB dismiss from the *reader*!)
[reader dismissModalViewControllerAnimated: YES];
parseHTML = [[NSXMLParser alloc] initWithContentsOfURL:[NSURL URLWithString:[#"http://url.com/suche/?q=" stringByAppendingString:symbol.data]] ];
NSLog(#"parser 1 start");
[parseHTML setDelegate:self];
[parseHTML parse];
NSLog(#"parser 1 ready");
[parseHTML release];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
for(NSString *key in [attributeDict allKeys]) {
if ([[attributeDict valueForKey:key] isEqualToString:#"search-result"]) {
link_is_here = true;
}
if ([key isEqualToString:#"href"] && link_is_here) {
product_link = [attributeDict valueForKey:key];
[parser abortParsing];
parseHTML2 = [[NSXMLParser alloc] initWithContentsOfURL:[NSURL URLWithString:[#"http://url.com" stringByAppendingString:product_link]]];
[parseHTML2 setDelegate:self];
parse_one_ok = true;
link_is_here = false;
[parseHTML2 parse];
}
if ([key isEqualToString:#"id"] && [[attributeDict valueForKey:key] isEqualToString:#"nutrition-allergy"]) {
allergy_is_here = true;
}
if ([key isEqualToString:#"title"] && allergy_is_here) {
NSLog(#"keys: %#",[attributeDict valueForKey:key]);
}
if ([key isEqualToString:#"id"] && [[attributeDict valueForKey:key] isEqualToString:#"another string"]) {
allergy_is_here = false;
parse_one_ok = true;
NSLog(#"Parser off");
[parser abortParsing];
}
}
}
-(void) parserDidEndDocument:(NSXMLParser *)parser{
if (parse_one_ok) {
[parseHTML2 release];
parse_one_ok = false;
}
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[parseHTML release];
[parseHTML2 release];
self.product_link = nil;
self.resultImage = nil;
self.resultText = nil;
[super dealloc];
}
#end
that is simple. You are releasing ParseHTML NSXMLPArsetwice.
-(void) imagePickerController: (UIImagePickerController*) reader
didFinishPickingMediaWithInfo: (NSDictionary*) info
in the lastline
-(void)dealloc.
A object should be release only if you have earned the ownership. by retain copy etc. But you have allocated it only once so should release only once. But you did two releases .
You are also releasing NSXMLParser object parseHTML2 thrice. As per your code at any stage parseHTML2 will be released at least twice which is retained only once. ParseHTML1 objects case have been mentioned above
Regards,
Jackson Sunny Rodrigues
Turn on NSZombieEnabled. You are obviously releasing something you shouldn't be. When you do this, it will show you exactly where the bad access is occurring and you can trace back to where you are releasing the object. Check out this tutorial:
http://www.codza.com/how-to-debug-exc_bad_access-on-iphone
Best to learn how to fix it and what's wrong :)

iPhone: memory leak on autoreleased object?

I am using the XMLParser class, which contains an array with XMLElement objects. The XMLElement is being allocated using the autorelease operation. However, for some reason I'm getting a memory leak (using instruments) on this line:
self.currentElement = [[[XMLElement alloc] init] autorelease];
I'm also releasing the XMLParser object in the caller class. I've highlighted the "problematic" lines in the XMLParser source code below as comment.
I really tried everything to fix it, but unfortunately I do not understand why this is happening at all. Any help is appreciated!
Thank you very much!
// --- XMLElement
#import <Foundation/Foundation.h>
#interface XMLElement : NSObject {
NSDictionary *attributes;
NSMutableArray *children;
NSString *textValue;
NSString *tagName;
XMLElement *parentElement;
}
-(id) init;
-(void) addChild:(XMLElement*) child;
#property(nonatomic, retain) NSDictionary *attributes;
#property(nonatomic, retain) NSMutableArray *children;
#property(nonatomic, retain) NSString *textValue;
#property(nonatomic, retain) NSString *tagName;
#property(nonatomic, retain) XMLElement *parentElement;
#end
#import "XMLElement.h"
#implementation XMLElement
#synthesize attributes, children, textValue, parentElement, tagName;
-(id)init {
if(self = [super init]) {
children = [[NSMutableArray alloc] init];
}
return self;
}
-(void) addChild:(XMLElement*) child{
[self.children addObject:child];
}
- (void)dealloc {
[attributes release];
[children release];
[textValue release];
[tagName release];
[parentElement release];
[super dealloc];
}
#end
// --- XMLParser
#import <Foundation/Foundation.h>
#import "XMLElement.h"
#interface XMLParser : NSObject<NSXMLParserDelegate> {
XMLElement *currentElement;
XMLElement *currentParentElement;
NSMutableString *currentElementValue;
}
- (BOOL)parseData: (NSData*) dataToParse;
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict;
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string;
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName;
#property (nonatomic, retain) XMLElement *currentParentElement;
#property (nonatomic, retain) XMLElement *currentElement;
#property (nonatomic, retain) NSMutableString *currentElementValue;
#end
#import "XMLParser.h"
#implementation XMLParser
#synthesize currentElementValue, currentElement, currentParentElement;
- (BOOL)parseData: (NSData*) dataToParse {
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:dataToParse];
[parser setDelegate:self];
BOOL success = [parser parse];
[parser release];
return success;
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict{
if(currentElement){
self.currentParentElement = currentElement;
}
// ------------------------------------------------------------
// Instruments is marking this line as source of the leak with 90%
self.currentElement = [[[XMLElement alloc] init] autorelease];
// --------
currentElement.tagName = elementName;
currentElement.attributes = attributeDict;
currentElement.parentElement = self.currentParentElement;
if(self.currentParentElement){
[self.currentParentElement addChild:currentElement]; // and this one with 10%
}
self.currentElementValue = nil;
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
if(!currentElement) {
return;
}
if(currentElementValue == nil)
self.currentElementValue = [NSMutableString stringWithString:string];
else
[currentElementValue appendString:string];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if(currentElement == nil){
if( currentParentElement.parentElement){
self.currentParentElement = currentParentElement.parentElement;
}
}else{
currentElement.textValue = currentElementValue;
[currentElementValue release];
currentElementValue = nil;
self.currentParentElement = currentElement.parentElement;
currentElement = nil;
}
}
- (void)dealloc {
[currentParentElement release];
[currentElement release];
[super dealloc];
}
#end
You must release currentElement in your dealloc method in XMLParser.
It's created as autorelease but then assigned to a retain property, so you are actually retaining it once (which is good).
UPDATE: you should do self.currentElement = nil; when you are done with it, not currentElement = nil;.
This:
self.currentElement = [[[XMLElement alloc] init] autorelease];
retains currentElement (because the property is declared with retain).
Later, in parser:didEndElement:namespaceURI:qualifiedName:, you do this:
currentElement = nil;
which causes the leak because you are not releasing currentElement.
From what I see on this page, this is a documented apple bug. I have experienced the same problem in some of my apps...
Is your XMLParser being run on a background thread? If so, you need to create an NSAutoReleasePool for that thread in order for your [[[XMLElement alloc] init] autorelease] call to work.
HI guys.
the reason is just one simple thing,
instead of
#property(nonatomic, retain) XMLElement *parentElement;
must be
#property(nonatomic, assign) XMLElement *parentElement;
also you need delete [parentElement release] from dealloc
reason is that you are creating cyclic referances. parent to child and chils to parent
and when you are realesing parser object the elements still ocurrs in memory with pointer xount > 0

iphone xml parser error

I'm trying to implement and parsing xml document using web service in my iphone application:
Here is my Request (HTTP GET):
http://api.stlouisfed.org/fred/category?category_id=125&api_key=78da607fc8224651eca5653e65a4be5e
Response will be:
<?xml version="1.0" encoding="utf-8" ?>
<categories>
<category id="125" name="Trade Balance" parent_id="13"/>
</categories>
my testxml.h :
#import <UIKit/UIKit.h>
#interface testxmlViewController : UIViewController {
IBOutlet UILabel *lbl1;
IBOutlet UILabel *lbl2;
IBOutlet UIButton *submit;
NSMutableData *categoryData;
NSURLConnection *URLConnection;
NSMutableString *currnetCategory;
BOOL *hello;
}
#property (nonatomic, retain) NSMutableData *categoryData;
#property (nonatomic, retain) NSURLConnection *URLConnection;
#property (nonatomic, retain) NSMutableString *currnetCategory;
- (IBAction) submitClick :(id) sender;
#end
my testxml.m file:
#import "testxmlViewController.h"
#implementation testxmlViewController
#synthesize categoryData, URLConnection;
#synthesize currnetCategory;
- (IBAction) submitClick :(id) sender
{
lbl1.text = #"Submit button was clicked.";
static NSString *URLString = #"http://api.stlouisfed.org/fred/category?category_id=125&api_key=78da607fc8224651eca5653e65a4be5e";
NSURLRequest *URLRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:URLString]];
URLConnection = [[[NSURLConnection alloc] initWithRequest:URLRequest delegate:self] autorelease];
NSAssert(self.URLConnection != nil, #"Failure to create URL connection.");
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
self.categoryData = [NSMutableData data];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[categoryData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"ERROR with theConenction");
[connection release];
[categoryData release];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *theXML = [[NSString alloc] initWithBytes: [categoryData mutableBytes] length:[categoryData length] encoding:NSUTF8StringEncoding];
NSLog(theXML);
[theXML release];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:categoryData];
if( parser )
{
[parser release];
}
parser = [[NSXMLParser alloc] initWithData: categoryData];
[parser setDelegate: self];
[parser setShouldResolveExternalEntities: YES];
[parser parse];
[connection release];
[categoryData release];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
NSLog(elementName);
if ([elementName isEqualToString:#"category"])
{
lbl1.text = [attributeDict objectForKey:#"name"];
NSLog([attributeDict objectForKey:#"name"]);
//currnetCategory = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if (currnetCategory)
{
[currnetCategory appendString:string];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
NSLog(elementName);
}
I'm getting all the values printed correctly in my **GDB :**
<?xml version="1.0" encoding="utf-8" ?>
<categories>
<category id="125" name="Trade Balance" parent_id="13"/>
</categories>
2010-03-11 10:05:57.817 testxml[16203:207] categories
2010-03-11 10:05:57.819 testxml[16203:207] category
2010-03-11 10:05:57.822 testxml[16203:207] Trade Balance
2010-03-11 10:05:57.823 testxml[16203:207] category
2010-03-11 10:05:57.823 testxml[16203:207] categories
But it was terminating application after execution of didEndElement method.
Will you please help me to solve this problem.
Thanks.
view-source: http://api.stlouisfed.org/fred/category?category_id=125&api_key=78da607fc8224651eca5653e65a4be5e
only shows
<categories>
<category id="125" name="Trade Balance" parent_id="13"/>
</categories>
Probably not the cause but an observation anyway :-)

XML Parsing in Cocoa Touch/iPhone

Okay i have seen TouchXML, parseXML, NSXMLDocument, NSXMLParser but i am really confused with what to to do.
I have an iphone app which connects to a servers, requests data and gets XML response. Sample xml response to different set of queries is give at http://pastebin.com/f681c4b04
I have another classes which acts as Controller (As in MVC, to do the logic of fetch the data). This class gets the input from the View classes and processes it e.g. send a request to the webserver, gets xml, parses xml, populates its variables (its a singleton/shared Classes), and then responses as true or false to the caller. Caller, based on response given by the controller class, checks controller's variables and shows appropriate contents to the user.
I have the following Controller Class variables:
#interface backendController : NSObject {
NSMutableDictionary *searchResults, *plantInfoResults, *bookmarkList, *userLoginResult;
}
and functions like getBookmarkList, getPlantInfo. Right now i am printing plain XML return by the webserver by
NSLog(#"Result: :%#" [NSString stringWithContentsOfURL:url])
I want a generic function which gets the XML returned from the server, parseses it, makes a NSMutableDictionary of it containing XML opening tags' text representation as Keys and XML Tag Values as Values and return that.
Only one question, how to do that?.
Have you tried any of the XML Parsers you mentioned? This is how they set the key value of a node name:
[aBook setValue:currentElementValue forKey:elementName];
P.S. Double check your XML though, seems you are missing a root node on some of your results. Unless you left it out for simplicity.
Take a look at w3schools XML tutorial, it should point you in the right direction for XML syntax.
Consider the following code snippet, that uses libxml2, Matt Gallagher's libxml2 wrappers and Ben Copsey's ASIHTTPRequest to parse an HTTP document.
To parse XML, use PerformXMLXPathQuery instead of the PerformHTTPXPathQuery I use in my example.
The nodes instance of type NSArray * will contain NSDictionary * objects that you can parse recursively to get the data you want.
Or, if you know the scheme of your XML document, you can write an XPath query to get you to a nodeContent or nodeAttribute value directly.
ASIHTTPRequest *request = [ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:#"http://stackoverflow.com/"];
[request start];
NSError *error = [request error];
if (!error) {
NSData *response = [request responseData];
NSLog(#"Root node: %#", [[self query:#"//" withResponse:response] description]);
}
else
#throw [NSException exceptionWithName:#"kHTTPRequestFailed" reason:#"Request failed!" userInfo:nil];
[request release];
...
- (id) query:(NSString *)xpathQuery withResponse:(NSData *)respData {
NSArray *nodes = PerformHTMLXPathQuery(respData, xpathQuery);
if (nodes != nil)
return nodes;
return nil;
}
providing you one simple example of parsing XML in Table, Hope it would help you.
//XMLViewController.h
#import <UIKit/UIKit.h>
#interface TestXMLViewController : UIViewController<NSXMLParserDelegate,UITableViewDelegate,UITableViewDataSource>{
#private
NSXMLParser *xmlParser;
NSInteger depth;
NSMutableString *currentName;
NSString *currentElement;
NSMutableArray *data;
}
#property (nonatomic, strong) IBOutlet UITableView *tableView;
-(void)start;
#end
//TestXMLViewController.m
#import "TestXmlDetail.h"
#import "TestXMLViewController.h"
#interface TestXMLViewController ()
- (void)showCurrentDepth;
#end
#implementation TestXMLViewController
#synthesize tableView;
- (void)start
{
NSString *xml = #"<?xml version=\"1.0\" encoding=\"UTF-8\" ?><Node><name>Main</name><Node><name>first row</name></Node><Node><name>second row</name></Node><Node><name>third row</name></Node></Node>";
xmlParser = [[NSXMLParser alloc] initWithData:[xml dataUsingEncoding:NSUTF8StringEncoding]];
[xmlParser setDelegate:self];
[xmlParser setShouldProcessNamespaces:NO];
[xmlParser setShouldReportNamespacePrefixes:NO];
[xmlParser setShouldResolveExternalEntities:NO];
[xmlParser parse];
}
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
NSLog(#"Document started");
depth = 0;
currentElement = nil;
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
NSLog(#"Error: %#", [parseError localizedDescription]);
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict
{
currentElement = [elementName copy];
if ([currentElement isEqualToString:#"Node"])
{
++depth;
[self showCurrentDepth];
}
else if ([currentElement isEqualToString:#"name"])
{
currentName = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:#"Node"])
{
--depth;
[self showCurrentDepth];
}
else if ([elementName isEqualToString:#"name"])
{
if (depth == 1)
{
NSLog(#"Outer name tag: %#", currentName);
}
else
{
NSLog(#"Inner name tag: %#", currentName);
[data addObject:currentName];
}
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if ([currentElement isEqualToString:#"name"])
{
[currentName appendString:string];
}
}
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
NSLog(#"Document finished", nil);
}
- (void)showCurrentDepth
{
NSLog(#"Current depth: %d", depth);
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
data = [[NSMutableArray alloc]init ];
[self start];
self.title=#"XML parsing";
NSLog(#"string is %#",data);
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
`enter code here`return [data count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
cell.textLabel.text = [data objectAtIndex:indexPath.row];
return cell;
}
#end