I need help with parsing a xml file. My problem is I don't know how to implement the didEndElement delegate.
What I want is that I will have 2 cells where Old Testament and New Testament will be displayed and then the Books of the Bible and the the chapters.
If I can just get some help with the xml parsing the rest I can manage.
Will be very grateful for any help!
Thanks and regards!
My xml file is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<bible>
<testament name="Old Testament">
<book name="Genesis">
<chapter id="Genesis 1"></chapter>
<chapter id="Genesis 2"></chapter>
</book>
<book name="Exodus">
<chapter id="Exodus 1"></chapter>
<chapter id="Exodus 2"></chapter>
</book>
</testament>
<testament name="New Testament">
<book name="Matthew">
<chapter id="Matthew 1"></chapter>
<chapter id="Matthew 2"></chapter>
</book>
<book name="Revelation">
<chapter id="Revelation 1"></chapter>
<chapter id="Revelation 2"></chapter>
</book>
</testament>
</bible>
// Bible.h
#import <Foundation/Foundation.h>
#interface Bible : NSObject {
NSMutableArray *bible;
NSMutableArray *testament;
NSMutableArray *book;
NSString *chapterID;
}
#property (nonatomic, retain)NSMutableArray *bible;
#property (nonatomic, retain)NSMutableArray *testament;
#property (nonatomic, retain)NSMutableArray *book;
#property (nonatomic, retain)NSString *chapterID;
#end
// Bible.m
#import "Bible.h"
#implementation Bible
#synthesize bible;
#synthesize testament;
#synthesize book;
#synthesize chapterID;
- (void) dealloc {
[bible release];
[testament release];
[book release];
[chapterID release];
[super dealloc];
}
#end
//
// XMLParser.h
// BibleXML
//
#import <Foundation/Foundation.h>
#import "Bible.h"
#protocol NSXMLParserDelegate;
#class BibleXMLAppDelegate, Bible;
#interface XMLParser : NSObject <NSXMLParserDelegate> {
NSMutableString *currentElementValue;
BibleXMLAppDelegate *appDelegate;
Bible *theBible;
}
- (XMLParser *) initXMLParser;
#end
//
// XMLParser.m
#import "XMLParser.h"
#import "BibleXMLAppDelegate.h"
#import "Bible.h"
#implementation XMLParser
- (XMLParser *) initXMLParser {
[super init];
appDelegate = (BibleXMLAppDelegate *) [[UIApplication sharedApplication] delegate];
return self;
}
- (void)parserDidStartDocument:(NSXMLParser *)parser{
NSLog(#"found file and started parsing");
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict {
if([elementName isEqualToString:#"bible"]) {
NSLog(#"Found element: %#", elementName);
appDelegate.bible = [[NSMutableArray alloc] init];
}
else if([elementName isEqualToString:#"testament"]) {
theBible = [[Bible alloc] init];
//Extract the attribute here.
theBible.testament = [attributeDict valueForKey:#"name"];
NSLog(#"Testament: %#", theBible.testament);
return;
}
else if ([elementName isEqualToString:#"book"])
{
theBible.book = [attributeDict valueForKey:#"name"];
NSLog(#"Book: %#", theBible.book);
return;
}
else if([elementName isEqualToString:#"chapter"])
{
theBible.chapterID =[attributeDict objectForKey:#"id"];
NSLog(#"Chapter: %#", theBible.chapterID);
return;
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if([elementName isEqualToString:#"bible"]){
return;
}
}
- (void) dealloc {
[theBible release];
[currentElementValue release];
[super dealloc];
}
#end
Following is the output from the debugger console:
2010-12-08 19:53:10.101 BibleXML[25641:207] found file and started parsing
2010-12-08 19:53:10.102 BibleXML[25641:207] Found element: bible
2010-12-08 19:53:10.103 BibleXML[25641:207] Testament: Old Testament
2010-12-08 19:53:10.103 BibleXML[25641:207] Book: Genesis
2010-12-08 19:53:10.104 BibleXML[25641:207] Chapter: Genesis 1
2010-12-08 19:53:10.104 BibleXML[25641:207] Chapter: Genesis 2
2010-12-08 19:53:10.105 BibleXML[25641:207] Book: Exodus
2010-12-08 19:53:10.105 BibleXML[25641:207] Chapter: Exodus 1
2010-12-08 19:53:10.106 BibleXML[25641:207] Chapter: Exodus 2
2010-12-08 19:53:10.107 BibleXML[25641:207] Testament: New Testament
2010-12-08 19:53:10.107 BibleXML[25641:207] Book: Matthew
2010-12-08 19:53:10.108 BibleXML[25641:207] Chapter: Matthew 1
2010-12-08 19:53:10.108 BibleXML[25641:207] Chapter: Matthew 2
2010-12-08 19:53:10.109 BibleXML[25641:207] Book: Revelation
2010-12-08 19:53:10.109 BibleXML[25641:207] Chapter: Revelation 1
2010-12-08 19:53:10.110 BibleXML[25641:207] Chapter: Revelation 2
2010-12-08 19:53:10.110 BibleXML[25641:207] No Errors
You're already parsing it. In the didEndElement: call, just do whatever you want to do with the element. Since your XML doesn't contain any wrapped strings (you're not using foundCharacters:), all you have to do is respond to didStartElement: and didEndElement: accordingly. If you need to capture attributes or allocate a new data structure to hold possible children, do it in didStartElement:. If you need to save off objects into collections or somehow finish processing of a particular element, do it in didEndElement:.
This question isn't really about parsing, it's about whatever logic you want to enact in response to parsing.
Edit in response to comment below:
I typically do the following to save off objects during parsing: in my interface, I declare the collection I need to save objects into and a temporary object that I used to hold whatever data I need to before adding it to the collection, like this
#interface MyClass : NSObject <NSXMLParserDelegate>{
NSMutableArray *collection_;
SomeObject *tempObject_;
}
#end
In the implementation I manipulate these two objects, typically in didStartDocument:, didStartElement: and didEndElement:, like so:
- (void)parserDidStartDocument:(NSXMLParser *)parser {
collection_ = [[NSMutableArray alloc] init];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict {
//if your object is tied to a tag that wraps text (delivered in foundCharacters:), initialize it here
tempObject_ = [[SomeObject alloc] init];
//maybe you need the attributes....
tempObject_.someProperty = [attributes objectForKey:#"attribute-name"];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
//when the tag ends, you can save it off into the collection
[collection_ addObject:tempObject_];
[tempObject_ release];
tempObject_ = nil;
}
Then do what you will with the collection object. Make sure you handle memory things, like releasing the collection object or whatever. I usually use something like a delegate callback (of my own devising) to get the collection to the model, in order to logically separate parsing from the model.
Related
I have an xml file that looks like this:
<result>
<trip duration="03:30">
<takeoff date="2010-06-19" time="18:40" city="Moscow"/>
<landing date="2010-06-19" time="20:10" city="Novgorod"/>
<flight carrier="Rossiya" number="8395" eq="320"/>
<price>13429.00</price>
</trip>
<trip duration="03:40">
<takeoff date="2010-06-19" time="09:20" city="Omsk"/>
<landing date="2010-06-19" time="11:15" city="Paris"/>
<flight carrier="AirFrance" number="1145" eq="320"/>
<price>13229.00</price>
</trip>
<trip duration="03:50">
<takeoff date="2010-06-19" time="07:20" city="Omsk"/>
<landing date="2010-06-19" time="14:15" city="Barcelona"/>
<flight carrier="AirFrance" number="1100" eq="320"/>
<price>13329.00</price>
</trip>
</result>
I'd like to get all the parameters and place them in 3 UITableViewCells. As you can see there are 3 flights and the data about them.
I've been trying to parse it in a TableView but I only managed to get the <price> thing going.
How would you deal with parsing complex-structured parameters within an XML file? I mean how would I get takeoff date parameter and so on and so forth?
As far as I could get you can't apply the tactics I'm using like this one:
- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementname isEqualToString:#"price"])
{
currentTweet.dateCreated = currentNodeContent;
}
Any help would be highly appreciated. Thanks in advance.
This is the code that I frequently use to build a dictionary from an XML file that follows this basic model of some well defined repeating element (in this case, "trip"), and a series of data elements within that, some of which I'm reading the attributes of the tag (in this case, "takeoff", "landing" and "flight"), and others I'm reading the data between the opening and closing tags (in this case, just "price").
I have the following ivars:
#interface XmlParserViewController () <NSXMLParserDelegate>
{
NSMutableArray *trips;
NSMutableDictionary *currentTrip;
NSMutableString *currentElement;
}
#end
And then the code looks like:
- (void)viewDidLoad
{
[super viewDidLoad];
trips = [[NSMutableArray alloc] init];
// I'm getting my xml from my bundle. You get it however you're currently getting it.
NSString *filename = [[NSBundle mainBundle] pathForResource:#"results" ofType:#"xml"];
NSData *data = [NSData dataWithContentsOfFile:filename];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
[parser parse];
}
#pragma mark - NSXMLParserDelegate methods
#define kRowElementTag #"trip"
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
NSArray *attributeElementNames = #[#"takeoff", #"landing", #"flight"];
NSArray *foundCharacterElementNames = #[#"price"];
if ([elementName isEqualToString:kRowElementTag])
{
currentTrip = [[NSMutableDictionary alloc] init];
[trips addObject:currentTrip];
if (attributeDict)
[currentTrip setObject:attributeDict forKey:elementName];
}
else if (currentTrip)
{
if ([attributeElementNames containsObject:elementName])
{
if (attributeDict)
[currentTrip setObject:attributeDict forKey:elementName];
}
else if ([foundCharacterElementNames containsObject:elementName] && currentElement == nil)
{
// you can change this to just grab a few fields ... add whatever fields you want to this
currentElement = [[NSMutableString alloc] init];
[currentTrip setObject:currentElement forKey:elementName];
}
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:kRowElementTag])
{
currentTrip = nil;
}
else if (currentElement)
{
currentElement = nil;
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if (currentElement)
{
[currentElement appendString:string];
}
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
NSLog(#"%s error=%#", __FUNCTION__, parseError);
// we should handle the error here
}
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
NSLog(#"%s trips=%#", __FUNCTION__, trips);
// generally I immediately kick off the reload of the table, but maybe
// you want to grok the trips dictionary first.
//
// [self.tableView reloadData];
}
As you can guess, I'm trying to end up with that sort of nested array/dictionary structure that we've gotten used to parsing JSON files. Clearly, I don't like the fact that I have to identify some of the structure of the XML file up front in my code (the fact that the outer array has "trip" tags, that "takeoff", "landing", and "flight" have attributes but "price"` doesn't), etc. But this is a little better than my first attempts at XML parsing that hardcoded values all over the place. Sigh.
You have to manage didStartElement too. This is a sample metacode could be good for you:
-(void)parser:(NSXMLParser*)parser didStartElement:(NSString *)elementName namespaceURI:(NSString*)namespaceURI qualifiedName:(NSString*)qualifiedName attributes:(NSDictionary*)attributeDict {
if([elementName isEqualToString:#"trip"]) {
currentTweet.tripDuration = [attributeDict objectForKey:#"duration"];
} else if ([elementName isEqualToString:#"takeoff"]) {
currentTweet.takeoffDate = [attributeDict objectForKey:#"date"];
currentTweet.takeoffTime = [attributeDict objectForKey:#"time"];
currentTweet.takeoffCity = [attributeDict objectForKey:#"city"];
} else if ([elementName isEqualToString:#"landing"]) {
...............
} else if ...........
}
I am not able to totally understand the flow of NSXMLParser and the delegate methods associated to them. Is there any way or any example where a detailed explanation is made how parsing is done using NSXMLParser. I have another XML where i need to store the relevant qno, tin, tout and answer into respective strings after parsing the XMl. PFB the XML.
<?xml version="1.0" encoding="UTF-8"?>
<ParticipantService>
<Response>
<FileName>CustomerSkillsIntro</FileName>
<playlist>
<question answer="t" qno="1" tin="71" title="Greet" tout="73"/>
<question answer="t" qno="2" tin="74" title="Have Name Tag" tout="77"/>
<question answer="t" qno="3" tin="78" title="Greet" tout="83"/>
<question answer="t" qno="4" tin="109" title="Helping Do My Job" tout="112"/>
<question answer="t" qno="5" tin="131" title="Greet Happily" tout="134"/>
<question answer="t" qno="6" tin="141" title="Stay cheerful when resident is crabby" tout="144"/>
<question answer="t" qno="7" tin="151" title="Bond with the new resident" tout="154"/>
<question answer="t" qno="8" tin="161" title="Welcome cheerfully" tout="164"/>
<question answer="t" qno="9" tin="169" title="Offer Help" tout="172"/>
<question answer="t" qno="10" tin="178" title="Help with interest" tout="181"/>
<question answer="t" qno="11" tin="183" title="Accompany" tout="186"/>
<question answer="t" qno="12" tin="189" title="Pay attention to 2 resudents" tout="192"/>
<question answer="t" qno="13" tin="199" title="Juggle the two accurately" tout="202"/>
<question answer="t" qno="14" tin="207" title="Bring in other help when needed" tout="212"/>
<question answer="t" qno="15" tin="219" title="Correct response I can ask" tout="222"/>
<question answer="t" qno="16" tin="231" title="Be charming" tout="237"/>
<question answer="t" qno="17" tin="247" title="Respond and delegate" tout="250"/>
<question answer="t" qno="18" tin="261" title="Apologize" tout="263"/>
<question answer="t" qno="19" tin="266" title="Offer activities" tout="270"/>
<question answer="t" qno="20" tin="273" title="Be sensitive to needs" tout="276"/>
<question answer="t" qno="21" tin="287" title="Offer anything you need" tout="290"/>
<question answer="t" qno="22" tin="311" title="Take off shoes, honor unusual request" tout="315"/>
<question answer="t" qno="23" tin="328" title="Always available menu explained" tout="331"/>
<question answer="t" qno="24" tin="333" title="Willing to stay beyond shift" tout="336"/>
<question answer="t" qno="25" tin="377" title="Explain policy" tout="380"/>
<question answer="t" qno="26" tin="390" title="Understand resident" tout="396"/>
</playlist>
<path>lmsstaging.2xprime.com</path>
<EncodedVideoURL>HTTP://lmsstaging.2xprime.com/test/vdos/Alzheimers.mp4</EncodedVideoURL>
</Response>
<RequestStatus>
<Code>1</Code>
<Status>SUCCESS</Status>
<Message/>
</RequestStatus>
</ParticipantService>
Can someone please explain me how to parse this XML and a detailed explanation about how NSXMLParser and the delegate methods work?? I want to store the "tin" and "tout" into an NSArray but i am not able to understand on how to parse it node by node. This would be very helpful.
NSXMLParser is a so called event based XML parser or SAX type parser. It starts reading your XML from the beginning, and every time it finds a new element, a closing element or character data, it informs you about it. This is done through the delegate and you have to specify what do you want to do if these event happens by implementing the callback functions. When parsing your example XML it will call more or less these functions:
parser:yourParser didStartElement:#"playlist" namespaceURI:#"" qualifiedName:#"" attributes:attribDict
// attribDict empty
parser:yourParser didStartElement:#"question" namespaceURI:#"" qualifiedName:#"" attributes:attribDict
// attribDict = {#"answer" -> #"t", #"qno" -> #"2", #"tin" -> #"71", #"title" -> #"Greet", #"tout" -> #"73"}
parser:yourParser didEndElement:#"question" namespaceURI:#"" qualifiedName:#""
// ...repeating the last two calls for each question...
parser:yourParser didEndElement:#"playlist" namespaceURI:#"" qualifiedName:#""
So, you should implement didStartElement something like this:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict {
if (elementName == #"question") {
// save the elements of attributeDict in your array
}
}
Couple of delegate methods are present for NSXML Parser-
-(BOOL) parse:(NSData *)xmlData
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
You can use following line of code in your case in method didStartElement-
if([elementName isEqualToString:#"question"]) {
NSString *answer = [attributeDict valueForKey:#"answer"];
NSString *qno = [attributeDict valueForKey:#"qno"];
NSString *tin = [attributeDict valueForKey:#"tin"];
NSString *title = [attributeDict valueForKey:#"title"];
// Now You can store these in your preferable data models - data objects/array/dictionary
}
EDIT -
#interface Question : NSObject
#property (nonatomic, retain) NSString *answer;
#property (nonatomic, retain) NSString *qno;
#property (nonatomic, retain) NSString *tin;
#property (nonatomic, retain) NSString *title;
#end
#implementation Question
#synthesize answer = _answer;
#synthesize qno = _qno;
#synthesize tin = _tin;
#synthesize title = _title;
- (void) dealloc {
self.answer = nil;
self.qno = nil;
self.tin = nil;
self.title = nil;
[super dealloc];
}
#end
Now in your didStartElement method -
NSMutableArray *questionsArray = [NSMutableArray array];
if([elementName isEqualToString:#"question"]) {
Question *questionObject = [[Question alloc] init];
questionObject.answer = [attributeDict valueForKey:#"answer"];
questionObject.answerqno = [attributeDict valueForKey:#"qno"];
questionObject.answertin = [attributeDict valueForKey:#"tin"];
questionObject.answertitle = [attributeDict valueForKey:#"title"];
[questionsArray addObject:questionObject];
[questionObject release];
}
You can create this array at class level and use where you want.
EDIT 2 - To extract the data from array-
//Suppose dataArray contains information -
for (int i = 0; i < [dataArray count]; i++) {
Question *obj = [dataArray objectAtIndex:i];
NSLog(#"%#",obj.answer);
NSLog(#"%#",obj.qno);
NSLog(#"%#",obj.tin);
NSLog(#"%#",obj.title);
}
All,
Disclaimer: I'm aware that there are other questions that ask similar questions, but none provide an answer that I understand or an answer that applies to my situation.
I have two classes, classA and classB. In classA there is a void instance method that creates a NSMutableDictionary (if you want specifics, it's a XMLparser). After classA's XMLParser is run and the NSMutableDictionary has been created and filled, classB is called which needs to do some other things with that dictionary. For some reason, classB cannot access the NSMutableDictionary in classA (actually, it can access it, but for some reason it shows up as "NULL"). What should I do?
Thanks in advance.
EDIT: You asked for the source code, you got it. ClassA below, dictionary in question is called "response."
#import "XMLParser.h"
#import "CardSetupViewController.h"
#implementation XMLParser
#synthesize response;
- (XMLParser *) initXMLParser
{
self = [super init];
// init dictionary of response data
response = [[NSMutableDictionary alloc] init];
return self;
}
//Gets Start Element of SessionData
- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqualToString:#"SessionData"])
{
NSLog(#"Found SessionData in the return XML! Continuing...");
//response is a NSMutableArray instance variable
//THIS SHOULD NEVER NEED TO BE USED
if (!response)//if array is empty, it makes it!
{
NSLog(#"Dictionary is empty for some reason, creating...");
response = [[NSMutableDictionary alloc] init];
}
//END: THIS SHOULD NEVER BE USED
return;
}
else
{
currentElementName = elementName;
NSLog(#"Current Element Name = %#", currentElementName);
return;
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if (!currentElementValue) {
// init the ad hoc string with the value
currentElementValue = [[NSMutableString alloc] initWithString:string];
} else {
[currentElementValue setString:string];
NSLog(#"Processing value for : %#", string);
}
}
//Gets End Element of SessionData
- (void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#"SessionData"])
{
// We reached the end of the XML document
// dumps dictionary into log
NSLog(#"Dump:%#", [response description]);
return;
}
else
{
//Adds key and object to dictionary
[response setObject:currentElementValue forKey:currentElementName];
NSLog(#"Set values, going around again... brb.");
}
currentElementValue = nil;
currentElementName = nil;
}
#end
You probably want to look into using singletons.
http://www.galloway.me.uk/tutorials/singleton-classes/
http://pixeleap.com/?p=19
http://www.iphonedevsdk.com/forum/iphone-sdk-development/5302-how-make-global-variables-objectivec.html
Those should get you started, if you need clarification just ask.
Are currentElementName and currentElementValue ivars ? If so, every time you catch either an element or a value, you must then release your ivar and retain the new value you catch.
Also, as the comments are saying you didn't follow any rules about writing a correct initializer.
What about classB ? Isn't there some logic that makes your dictionary be released ?
Try managing a bit more the memory.
you have several options... so many options actually
Option A:(set)
#interface ClassB{
NSDictionary * someDict;
}
-(void)setSomeDict:(NSDictionary *)aDict;
#end
#implementation ClassB
-(void)setSomeDict:(NSDictionary *)aDict
{
someDict = [aDict retain];//or copy depending on your needs;
}
#end
somewhere in ClassA...
ClassB * b = [ClassB new];
[b setSomeDict: someOtherDict];
Option B:(push)
#interface ClassB{
NSDictionary * someDict;
}
-(void)doSomethingWithDict:(NSDictionary *)aDict;
#end
#implementation ClassB
-(void)doSomethingWithDict:(NSDictionary *)aDict
{
NSLog(#"did something with aDict: %#",aDict);
}
#end
somewhere in ClassA...
ClassB * b = [ClassB new];
[b doSomethingWithDict: someOtherDict];
Option c:(init)
#interface ClassB{
NSDictionary * someDict;
}
-(id)initWithDict:(NSDictionary *)aDict;
#end
#implementation ClassB
-(id)initWithDict:(NSDictionary *)aDict
{
self = [super init];
if(self)
{
someDict = [someDict retain]; //or copy depending on your needs
}
NSLog(#"did something with aDict: %#",aDict);
}
#end
somewhere in ClassA...
ClassB * b = [[ClassB alloc] initWithDict:someOtherDict];
[b doSomethingelse];
you can also use properties etc... there are many many options, but you should understand object ownership as well so you don't end up leaking the dictionary.
-(void)dealloc
{
[someDict release];
}
should be added to the classB.
First of all check if u get the response in this delegate method of NSXMLParser
- (void)parserDidEndDocument:(NSXMLParser *)parser;
and send the response via a delegate (which u should implement in this class) and the delegate method is to implemented in the other class ...Simple !!!
OK this should be an easy one but still im breaking my head here:
In my root view controller I have a NSString called "entry" and is working perfectly. I NSLogged it and it works.
I have another class called ´ParseOperation´ and in it i have a NSStringcalled "localEntry" and im trying to send to "ParseOperation" the variable "entry" from "RootViewController" this is my RootViewController code for that:
RootViewController.m
ParseOperation *parseOperation = [[ParseOperation alloc] init];
parseOperation.localEntry = entry;
It just doesn't work. If I NSLog in my ParseOperation.m it returns "null", but if i do it on my RootViewController it returns the correct variable. and yes i did imported the ParseOperation.h
Here is the ParseOperation code (only the part that uses localEntry):
ParseOperation.h
#interface ParseOperation : NSOperation <NSXMLParserDelegate>
{
NSString *localEntry;
}
#property (nonatomic, retain) NSString *localEntry;
#end
ParseOperation.m
#synthesize localEntry;
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict
{
//NSLog(#"entrada %#",localEntry);
if ([elementName isEqualToString:localEntry])
{
self.workingEntry = [[[AppRecord alloc] init] autorelease];
}
storingCharacterData = [elementsToParse containsObject:elementName];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
{
if (self.workingEntry)
{
if (storingCharacterData)
{
NSString *trimmedString = [workingPropertyString stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
[workingPropertyString setString:#""]; // clear the string for next time
if ([elementName isEqualToString:kIDStr])
{
self.workingEntry.appURLString = trimmedString;
}
else if ([elementName isEqualToString:kNameStr])
{
self.workingEntry.appName = trimmedString;
}
else if ([elementName isEqualToString:kImageStr])
{
self.workingEntry.imageURLString = trimmedString;
}
else if ([elementName isEqualToString:kArtistStr])
{
self.workingEntry.artist = trimmedString;
}
}
else if ([elementName isEqualToString:localEntry])
{
[self.workingArray addObject:self.workingEntry];
self.workingEntry = nil;
}
}
}
THANKS!
In all likelihood, rootViewController is nil. When you declare and synthesize a property, it only creates the getter/setter methods for you. It does not initialize the variable to anything.
Since objective-c allows you to message nil, you won't crash when you write:
NSString *localentry = rootViewController.entry;
Messaging nil just returns nil. So if rootViewController is nil, then localentry will be nil as well.
Make sure you're actually setting rootViewController for this class. For example,
ParseOperation *myOperation = [[ParseOperation alloc] init];
[myOperation setRootViewController:rootViewController];
Or, make sure you've established the outlet connection in Interface Builder. In any case, I'd suspect rootViewController is nil. (You can test this with NSLog statements).
Are you sure, since it is an IBOutlet, that you connected to it in interface builder?
To answer my own question I just had to connect the viewController and the ParseOperation programmatically by adding the following to my header in the parseOperation:
#class RootViewController;
RootViewController *rootViewController;
#property (nonatomic, retain) IBOutlet RootViewController *rootViewController;
And the following on the m file of the ParseOperation:
#import "RootViewController.h"
rootViewController = [[RootViewController alloc]init];
After that in the parse operation I just declared:
localEntry= rootViewContoller.entry;
Hey,
I have to parse XML in my iOS app. I took Apple's SeismicXML Sample as my base, but I'm experiencing a really strange behaviour.
These are my parser methodes:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict {
if ([elementName isEqualToString:kEntryElementName]) {
Photo *photo = [[Photo alloc] init];
self.currentPhotoObject = photo;
[photo release];
} else if ([elementName isEqualToString:kTitleElementName] ||
[elementName isEqualToString:kLocationElementName] ||
[elementName isEqualToString:kAuthorElementName]) {
accumulatingParsedCharacterData = YES;
[currentParsedCharacterData setString:#""];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:kEntryElementName]) {
NSLog(#"Did End - Titel:%#", self.currentPhotoObject.titleText);
NSLog(#"Did End - Location:%#", self.currentPhotoObject.locationText);
NSLog(#"Did End - Author:%#", self.currentPhotoObject.author);
[self.currentParseBatch addObject:self.currentPhotoObject];
parsedPhotosCounter++;
if ([self.currentParseBatch count] >= kMaximumNumberOfPhotosToParse) {
[self performSelectorOnMainThread:#selector(addPhotosToList:)
withObject:self.currentParseBatch
waitUntilDone:NO];
self.currentParseBatch = [NSMutableArray array];
}
}
else if ([elementName isEqualToString:kTitleElementName]) {
self.currentPhotoObject.titleText = self.currentParsedCharacterData;
}
else if ([elementName isEqualToString:kAuthorElementName]) {
self.currentPhotoObject.author = self.currentParsedCharacterData;
}
else if ([elementName isEqualToString:kLocationElementName]) {
self.currentPhotoObject.locationText = self.currentParsedCharacterData;
}
accumulatingParsedCharacterData = NO;
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if (accumulatingParsedCharacterData) {
// If the current element is one whose content we care about, append 'string'
// to the property that holds the content of the current element.
//
[self.currentParsedCharacterData appendString:string];
}
}
Everything works great, the XML Data comes correctly. The parser parses everything as it should.
The problem is in the parser didEndElement methode.
else if ([elementName isEqualToString:kTitleElementName]) {
self.currentPhotoObject.titleText = self.currentParsedCharacterData;
}
When I get "self.currentPhotoObject.titleText" via NSLog, I get the right parsed Data. But then:
else if ([elementName isEqualToString:kAuthorElementName]) {
self.currentPhotoObject.author = self.currentParsedCharacterData;
}
When I get the NSLog of "self.currentPhotoObject.titleText" and from "self.currentPhotoObject.author" here, both give me the author.
In the third parsed methode it is the same. All three properties (titleText, author and locationText) are the locationText.
I have no idea why .titleText e.g. is changed when the parser sets .author.
I have doublechecked everything at least 10 times and compared it to the SeismicXML sample but I can't find the problem.
Please help me. I'm thankfull for every hint !
Greets Sebastian
ps: My properties in the .m file:
#interface ParseOperation () <NSXMLParserDelegate>
#property (nonatomic, retain) Photo *currentPhotoObject;
#property (nonatomic, retain) NSMutableArray *currentParseBatch;
#property (nonatomic, retain) NSMutableString *currentParsedCharacterData;
#end
#implementation ParseOperation
#synthesize photoData, currentPhotoObject, currentParsedCharacterData, currentParseBatch;
It's because you assign same NSMutableString instance to all this properties.
1) Declare author, titleText, locationText properties as copy to avoid this in future.
2) Make a copy each time you want to return value of NSMutableString or assign it to something
self.currentPhotoObject.titleText = [[self.currentParsedCharacterData copy] autorelease];