Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

jeremyapp

macrumors newbie
Original poster
Apr 30, 2008
27
0
Hey everyone,

I'm just starting to get the hang of obj-c, but I still have a long way to go.

I have a pretty basic understanding of the terminology, but no matter what I've read I can't seem to find the answer to this question. To illustrate it best, let me give you a hypothetical situation:

Let's say I have a remote XML file on some webserver with the following format:

Code:
<?xml version="1.0" encoding="ISO-8859-1"?>
<quote>
     <id>1</id>
     <body>A penny saved is a penny earned.</body>
     <author>Unknown</author>
</quote>

In my interface, I have two text labels (one for quote, and one for author) and a button.

In my viewController header file, I've declared the UILabels for both labels, as well as two NSString's. I've also added an IBAction called changeLabel:, which is linked to my button.

What I would like to do is the following:

When the button i pressed, I would like for the remote XML file to be parsed in such a way that I end up with the info from the <body> tags in one NSString and the info from the <author> tags in the other NSString. From there, I know exactly how to change the labels - it's just getting that data from the remote XML that I can't wrap my head around.

If anyone could please offer some help, I would be very grateful. Thank you!
 

jeremyapp

macrumors newbie
Original poster
Apr 30, 2008
27
0
That was the first place that I looked, but it didn't quite have what I was looking for. While it probably would have been very helpful for someone with a bit more cocoa knowledge, I can't seem to understand how to just get single elements from xml instead of a whole array to put into a table.
 

mpramodjain

macrumors regular
Nov 20, 2008
152
0
Banglore
found Characters.

Hey everyone,

I'm just starting to get the hang of obj-c, but I still have a long way to go.

I have a pretty basic understanding of the terminology, but no matter what I've read I can't seem to find the answer to this question. To illustrate it best, let me give you a hypothetical situation:

Let's say I have a remote XML file on some webserver with the following format:

Code:
<?xml version="1.0" encoding="ISO-8859-1"?>
<quote>
     <id>1</id>
     <body>A penny saved is a penny earned.</body>
     <author>Unknown</author>
</quote>

In my interface, I have two text labels (one for quote, and one for author) and a button.

In my viewController header file, I've declared the UILabels for both labels, as well as two NSString's. I've also added an IBAction called changeLabel:, which is linked to my button.

What I would like to do is the following:

When the button i pressed, I would like for the remote XML file to be parsed in such a way that I end up with the info from the <body> tags in one NSString and the info from the <author> tags in the other NSString. From there, I know exactly how to change the labels - it's just getting that data from the remote XML that I can't wrap my head around.

If anyone could please offer some help, I would be very grateful. Thank you!

Get the current element name , in startElement callback, and in foundCharacters get that string into your variables.Thats all...
 

jeremyapp

macrumors newbie
Original poster
Apr 30, 2008
27
0
Thank you for the help. I tried this, but I'm still just not getting it. Do you mind showing me what the code should look like? Thanks.
 

drivefast

macrumors regular
Mar 13, 2008
128
0
wherever you start the parser:
Code:
	NSXMLParser *pXML = [[NSXMLParser alloc] initWithData:yourXMLstring];
	[pXML setDelegate:self];
	[pXML parse];
	if ([pXML parserError]) {
		// handle error here
	}
	[pXML release];
and since you set your delegate to self, these methods have to be declared in the same class:
Code:
- (void)parser:(NSXMLParser *)parser 
	didEndElement:(NSString *)elementName 
	namespaceURI:(NSString *)namespaceURI 
	qualifiedName:(NSString *)qName
{
	if ([elementName isEqualToString:@"id"]) {
		lblID.text = [NSString stringWithString:tagContents];
	} else if ([elementName isEqualToString:@"body"]) {
		lblBody.text = [NSString stringWithString:tagContents];
	// ... and so on with each tag name
	}
}
Code:
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
	[tagContents appendString:string];	
}
i use to declare tagContents as a NSMutableString and clear it off (set it to an empty string) in each parser:didStartElement:... pass.
 

jeremyapp

macrumors newbie
Original poster
Apr 30, 2008
27
0
Thanks for the help again! I feel like I'm getting close now. It still isn't working, though. Here's what I have:

Code:
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
	NSURL *xmlURL = [NSURL fileURLWithPath:@"http://www.quotestumbler.com/sample.xml"];
    NSXMLParser *pXML = [[ NSClassFromString(@"NSXMLParser") alloc] initWithContentsOfURL:xmlURL];
	[pXML setDelegate:self];
	[pXML parse];
	if ([pXML parserError]) {
		// handle error here
	}
	[pXML release];
}

- (void)parser:(NSXMLParser *)parser 
 didEndElement:(NSString *)elementName 
  namespaceURI:(NSString *)namespaceURI 
 qualifiedName:(NSString *)qName
{
	if ([elementName isEqualToString:@"id"]) {
		quote.text = [NSString stringWithString:tagContents];
	} else if ([elementName isEqualToString:@"body"]) {
		author.text = [NSString stringWithString:tagContents];
	}
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
	[tagContents appendString:string];	
}

It's saying that tagContents is undeclared. What could I be doing wrong? Thanks!
 

drivefast

macrumors regular
Mar 13, 2008
128
0
tagContents should be your variable. declare it as a mutable string in the header file, and take care of emptying its contents when needed. so in the declarations part of your class (in your header file) you would have
NSMutableString *tagContents;
and right before you initialize the parser you would do
tagContents = @"";
to give it a fresh start. then you would clear again the contents after a tag is completely processed, for example at the end of the parser:didEndElement:... function.

here's how the parser works. when you say [parser parse], the string you have given as an argument starts to be poured (streamed) in the parser, character by character. when a sequence that looks like "<mytag>" is encountered, the parser calls your parser:didStartElement:... function, and gives you a chance to prepare the stage for the tag in question. when you're done processing, the parser continues to pour the characters between <mytag> and </mytag>, occasionally calling the parser:foundCharacters: function. in your case, and in most of the cases, all you have to do is to concatenate the found characters into the value that you expect the tag to carry. finally, when the "</mytag>" construction is encountered, the parser calls your parser:didEndElement:... function, to give you a chance to do something with the data you've just put together. note that control is not given to the line after the [parser parse] command until after the whole xml document is done parsing; in other words, you can consider the "parse" command blocking. this is how a SAX parser works - there's another breed of parsers, called DOM, which swallows the whole xml doc and presents it in some form of a tree with node navigation methods, but as far as i know there is no DOM parser in the sdk.

my final comment is actually a rant: the acceptable inputs to the xml parser offered by the sdk is either a file or a string. why in the world cant we have a stream as an input??... it kinda defeats the very purpose of having a sax parser.
 

jeremyapp

macrumors newbie
Original poster
Apr 30, 2008
27
0
I'm so sorry about this, but it just still isn't working and I can't understand why! I feel really bad that I keep asking questions that are probably really simple and stupid, but this has been frustrating me to no ends. Here's my situation:

I have a view-based application.

In QuoteTestViewController.h:

Code:
//
//  QuoteTestViewController.h

#import <UIKit/UIKit.h>

@interface QuoteTestViewController : UIViewController {
	IBOutlet UITextView *quoteText;
	IBOutlet UILabel *authorLabel;
	
	NSMutableString *tagContents;
}

@property (nonatomic, retain) UITextView *quoteText;
@property (nonatomic, retain) UILabel *authorLabel;

@end

In QuoteTestViewController.m:

Code:
//
//  QuoteTestViewController.m

#import "QuoteTestViewController.h"

@implementation QuoteTestViewController
@synthesize authorLabel, quoteText;


/*
// The designated initializer. Override to perform setup that is required before the view is loaded.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
        // Custom initialization
    }
    return self;
}
*/

/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/



// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
	tagContents = @"";
    NSURL *xmlURL = [NSURL fileURLWithPath:@"http://www.quotestumbler.com/sample.xml"];
    NSXMLParser *pXML = [[ NSClassFromString(@"NSXMLParser") alloc] initWithContentsOfURL:xmlURL];
	[pXML setDelegate:self];
	[pXML parse];
	if ([pXML parserError]) {
		// handle error here
	}
	[pXML release];
}

- (void)parser:(NSXMLParser *)parser 
 didEndElement:(NSString *)elementName 
  namespaceURI:(NSString *)namespaceURI 
 qualifiedName:(NSString *)qName
{
	if ([elementName isEqualToString:@"author"]) {
		authorLabel.text = [NSString stringWithString:tagContents];
	} else if ([elementName isEqualToString:@"body"]) {
		quoteText.text = [NSString stringWithString:tagContents];
		// ... and so on with each tag name
	}
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
	[tagContents appendString:string];	
}

/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
    // Release anything that's not essential, such as cached data
}


- (void)dealloc {
    [super dealloc];
}

@end

My goal is to have it parse that xml file and put change the labels to the values of the "author" and "body" elements in my xml file. However, the labels aren't changing no matter what I try. Does it look like something is wrong here?

Again, I can't tell you how much I appreciate this. Thanks!
 

dejo

Moderator emeritus
Sep 2, 2004
15,982
452
The Centennial State
My goal is to have it parse that xml file and put change the labels to the values of the "author" and "body" elements in my xml file. However, the labels aren't changing no matter what I try. Does it look like something is wrong here?
Are the UITextView/UILabel text properties not getting updated or are they, but those changes to their values are not getting propogated to the display? (Put in a breakpoint when you update them to be sure). If the latter, you just may need to call something as simple as setNeedsDisplay on your UIView.
 

jeremyapp

macrumors newbie
Original poster
Apr 30, 2008
27
0
To test if it was even able to find the elements in the xml, I made it so that it would write a line to the console if it had found the element we were looking for:

Code:
- (void)parser:(NSXMLParser *)parser 
 didEndElement:(NSString *)elementName 
  namespaceURI:(NSString *)namespaceURI 
 qualifiedName:(NSString *)qName
{
	if ([elementName isEqualToString:@"author"]) {
		authorLabel.text = [NSString stringWithString:tagContents];
                 
                // Let us know if we found the element

                NSLog(@"Element found");
	} else if ([elementName isEqualToString:@"body"]) {
		quoteText.text = [NSString stringWithString:tagContents];
		NSLog(@"Element found");
	}
}

Nothing shows up in the console, so I think it's something with the parser.
 

drivefast

macrumors regular
Mar 13, 2008
128
0
as dejo suggested, put a breakpoint on the first line of the parser:didEndElement:... function and then step-debug (use the run menu to learn the commands). if your breakpoint is never touched, try implementing even a dummy parser:didStartElement:...function (although this is more of a shot in the dark).
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.