i have declared 1 array in application delegate
#property(strong,nonatomic)NSMutableArray *books;
i have added objects to this array in XMLParser.m
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if([elementName isEqualToString:#"Books"])
return;
NSLog(#"i m in didEndElement");
if([elementName isEqualToString:#"Book"]) {
[appDelegate.books addObject:aBook]; //here i have added objects to array
[aBook release];
aBook = nil;
NSLog(#"books=%#",appDelegate.books);
}
else
[aBook setValue:currentElementValue forKey:elementName];
[currentElementValue release];
currentElementValue = nil;
}
i have declared this array in XMLParser.h like this
#interface XMLParser : NSObject {
NSMutableString *currentElementValue;
AppDelegate *appDelegate;
Book *aBook;
}
now i have to access this array in BooksDetailViewController.m , how do i access this array.
in short how i can access array from application delegate into BooksDetailViewController
First of all you should have to alloc and init your array, then You can add objects in it.
You should have to create AppDelegate instance in your BooksDetailViewController like this,
in .h file
AppDelegate *appDelegate;
and in .m file
appDelegate = [[UIApplication sharedApplication]delegate];
Now you can access your array like this,
appDelegate.books
Related
This question might have been asked frequently but I have read almost all of them and yet couldn't figure out a solution for my case.
I plan to save the parsed data to a NSMutableDictionary. Parser works ok and If I get a log it shows all data parsed. the problem is the last Item only saves in the NSDictionary. I can guess that I placed the dictionary in a wrong method but I can't figure out a better solution for saving. I use dictionary in order to hold the name and the text of element. here is my code :
#implementation Parser
#synthesize currentElementPointer, rootElement;
#synthesize dictionary;
-(id)initParser
{
if(self = [super init]) {
tvc = (TimeTableViewController*)[[UIApplication sharedApplication]delegate];
APUAppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
context = [appDelegate managedObjectContext];
[self deleteAllObjects:#"TimeTable"];
}
return self;
}
#pragma mark -
#pragma mark PARSER
-(void)parserDidStartDocument:(NSXMLParser *)parser
{
self.parsing = YES;
}
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if([elementName isEqualToString:#"intake"])
{
NSString *name = attributeDict[#"name"];
self.parsing = [name isEqualToString:[self sendIntakeToParser]];
}
if(![self isParsing]) return;
if(self.rootElement == nil) {
self.rootElement = [[List alloc]init];
self.currentElementPointer = self.rootElement;
} else {
List *newList = [[List alloc]init];
newList.parent = self.currentElementPointer;
[self.currentElementPointer.subElements addObject:newList];
self.currentElementPointer = newList;
}
self.currentElementPointer.name = elementName;
self.currentElementPointer.attributes = attributeDict;
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if(![self isParsing]) return;
if([self.currentElementPointer.text length] > 0) {
self.currentElementPointer.text = [self.currentElementPointer.text stringByAppendingString:string];
} else {
self.currentElementPointer.text = string;
}
dictionary = [[NSMutableDictionary alloc]init];
[dictionary setObject:[self.currentElementPointer text] forKey:[self.currentElementPointer name]];
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if([self isParsing])
{
self.currentElementPointer = self.currentElementPointer.parent;
} else if ([elementName isEqualToString:#"intake"])
{
self.parsing = YES;
}
}
This is my xml structure:
The source of your problem is that you re-instantiate your dictionary with each call to parser:foundCharacters: which will be called at least once per element that has text. One thing you could do is move the dictionary instantiation to parser:didStartElement:... whenever elementName is equal to #"timetable", instantiate a mutable array in parser:didStartDocument: and then, in parser:didEndElement:..., if the elementName is `#"timetable", add it to your array.
An example of this is shown below:
Parser.h:
#import <Foundation/Foundation.h>
#interface Parser : NSObject <NSXMLParserDelegate>
- (void)parseData:(NSData *)data;
#end
Parser.m:
#import "Parser.h"
#interface Parser ()
#property (nonatomic,strong) NSMutableArray *timetableDictionaries;
#property (nonatomic,strong) NSMutableDictionary *currentTimetableDictionary;
#property (nonatomic,copy) NSString *currentElementName;
#end
#implementation Parser
- (void)parseData:(NSData *)data
{
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
[parser parse];
}
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
self.timetableDictionaries = [NSMutableArray array];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
self.currentElementName = elementName;
if ([elementName isEqualToString:#"timetable"]) {
self.currentTimetableDictionary = [NSMutableDictionary dictionary];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if (self.currentElementName) {
NSString *existingCharacters = self.currentTimetableDictionary[self.currentElementName] ? : #"";
NSString *nextCharacters = [existingCharacters stringByAppendingString:[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
if ([nextCharacters length] > 0) {
self.currentTimetableDictionary[self.currentElementName] = nextCharacters;
}
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:#"timetable"]) {
[self.timetableDictionaries addObject:self.currentTimetableDictionary];
self.currentTimetableDictionary = nil;
}
self.currentElementName = nil;
}
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
NSLog(#"timetable dictionaries = %#",self.timetableDictionaries);
}
#end
I tested the above class with the following file:
<weekof week="2013-07-29">
<intake name="APCF1304">
<timetable>
<date>SOME DATE</date>
<time>SOME TIME</time>
<lecturer>SOME LECTURER</lecturer>
</timetable>
<timetable>
<date>ANOTHER DATE</date>
<time>ANOTHER TIME</time>
<lecturer>ANOTHER LECTURER</lecturer>
</timetable>
</intake>
</weekof>
And it gave the following output:
2013-08-11 11:50:03.205 MyParser[25368:c07] timetable dictionaries = (
{
date = "SOME DATE";
lecturer = "SOME LECTURER";
time = "SOME TIME";
},
{
date = "ANOTHER DATE";
lecturer = "ANOTHER LECTURER";
time = "ANOTHER TIME";
}
)
However, you've built a tree of the document already, presumably for another purpose, so you could just re-use that at the end of your parsing to build your dictionaries. For example, if you just wanted dictionaries for each timetable, you could do something that looked like this, in pseudocode:
dictionaries = [new mutable array]
element = self.root
[extract dictionaries from: element to:dictionaries]
implementation of extractDictionariesFrom:element to:dictionaries:
if any subelement has non-nil text:
item = [new mutable dictionary]
for each sub element:
item[element name] = element text
[add item to dictionaries]
else
for each subelement:
[extract dictionaries from: subelement to: dictionaries]
Then you could discard the use of the mutable dictionary in your parser callback.
I previously asked this question XMLParser Advice.
However I am still unable to get it to function properly....
So I guess I will start from scratch:
Located at a certain URL is an XML Tree that looks like this
<result>
//stuff that I dont need
<title>
//thing that I do need
</title>
//stuff that I dont need
<body>
//thing that I do need
</body>
<result>
How the heck do I go about parsing that?
The (useless) code I have so far can be found in the link at the top of this question.
Thank you for your time.
Write a simple class, which will be the parser's delegate.
#interface YourObject : NSObject <NSXMLParserDelegate> {
NSString *title, *body; // object attributes
NSXMLParser *parser; // will parse XML
NSMutableString *strData; // will contains string data being parsed
}
#property(readwrite, copy) NSString *title, body;
// will be used to set your object attributes
-(void)fetchValuesAtURL:(NSString *)url;
#end
The fetchValuesAtURL: method will initiate the parse operation.
#implementation YourObject
#synthesize title, body;
-(id)init {
self = [super init];
if(self) {
title = #"";
body = #"";
parser = nil;
strData = [[NSMutableString alloc] initWithCapacity:10];
}
return self;
}
-(void)fetchValuesAtURL:(NSString *)url {
if(parser) {
[parser release];
}
NSURL *xmlURL = [NSURL URLWithString:url];
parser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];
[parser setDelegate:self];
[parser parse];
}
-(void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict {
// element is about to be parsed, clean the mutable string
[strData setString:#""];
}
// the probably missing method
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
// content (or part of) has been found, append that to the current string
[strData appendString:string];
}
-(void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName {
// element has been parsed, test the element name
// and store strData accordingly
if([elementName isEqualToString:#"title"]) {
self.title = strData;
}
else { // or else if, here you got two elements to parse
self.body = strData;
}
}
-(void)dealloc {
[title release];
[body release];
[strData release];
if(parser) {
[parser release];
}
[super dealloc];
}
#end
Then :
YourObject *obj = [[YourObject alloc] init];
[obj fetchValuesAtURL:#"http://www.site.com/xml/url"];
NSXMLParser's delegate is able to do many more things, as described in Event-Driven XML Programming Guide from Apple.
For complete reference on delegate methods, see NSXMLParserDelegate Protocol Reference.
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.
I have two controllers, SyncController and XMLController. SyncController sends some parameters to XMLController, which connects to an API and wraps the result as objects within an NSMutableArray, and returns the array back to the SyncController.
Some code:
SyncController.h
-(void)urlHandler:(NSArray *)urlHandler listObjectsFinishedLoading:(NSMutableArray *)resultData;
SyncController.m
- (void)urlHandler:(NSArray *)urlHandler listObjectsFinishedLoading:(NSMutableArray *)resultData;
{
NSMutableArray *receivedObjects = [[NSMutableArray alloc] init];
[receivedObjects addObjectsFromArray:resultData];
for (Object *o in receivedObjects) {
//DO SOME STUFF
}
[receivedObjects release];
}
XMLController.h
#interface XMLController : NSObject {
NSMutableArray *objects;
}
#property (nonatomic, retain) NSMutableArray *objects;
XMLController.m
-(void) connectionDidFinishLoading:(NSURLConnection *) connection {
objects = [[NSMutableArray alloc] init];
if ([delegate respondsToSelector:#selector(urlHandler:listObjectsFinishedLoading:)]) {
[delegate urlHandler:self listObjectsFinishedLoading:objects];
}
//[objects release];
}
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
// Initialize an Object
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
{
// Put some properties unto Object
// Ad Object to the objects array
// release Object
}
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
//[objects release];
}
- (void)dealloc {
//[objects release];
[super dealloc];
}
My question is, how do I properly release the objects array? If I don't release it, the code works properly (the actions from //DO SOME STUFF are executed) but it obviously leaks. If I release it, wherever I do it (see the commented //[objects release]; in three places) the app crashes.
Any suggestions? Thanks.
Try to
if ([delegate respondsToSelector:#selector(urlHandler:listObjectsFinishedLoading:)]) {
[delegate urlHandler:self listObjectsFinishedLoading:[objects autorelease]];
}
Maybe you're deallocating the Object at - (void)parserDidEndDocument:(NSXMLParser *)parser and again in - (void)dealloc. Try to set the object to nil (≠ NULL) when you release it, so you know it won't get released again.
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
[objects release], objects = nil;
}
-(void) connectionDidFinishLoading:(NSURLConnection *) connection {
NSMutableArray *temp=[[NSMutableArray alloc] init];
self.objects=temp;
[temp release];
if ([delegate respondsToSelector:#selector(urlHandler:listObjectsFinishedLoading:)]) {
[delegate urlHandler:self listObjectsFinishedLoading:objects];
}
//[objects release];
}
and then
- (void)dealloc {
//[objects release];
[self.objects release];
[super dealloc];
}
use in this way it definitely works.
You are defining objects as a retained property, and then addressing the instance variable directly. If you are using synthesize to generate the getters and setters, then let them do the memory management for you.
self.objects = [[[NSMutableArray alloc] init] autorelease];
and
self.objects = nil;
rather than manually doing the releases.
thanks for your help. Still doesn't work, but I get the feeling that teh problem might be in the Object class. Releasing the array calls release on every object right?
Object.h
#interface Object : NSObject {
NSString *name;
}
#property (nonatomic, retain) NSString *name;
Object.m
-(void) dealloc{
[self.name release];
[super dealloc];
}
If I comment the [self.name release]; line, then the array in question CAN be released, it doesn't crash, and with no leaks. But then the app leaks NSStrings in other places where the Object name property is used.
It might be something very trivial that I miss.
Thanks.
I am parsing an XML file for two elements: "title" and "noType". Once these are parsed, I am adding them to an object called aMaster, an instance of my own Master class that contains NSString variables.
I am then adding these instances to an NSMutableArray on a singleton, in order to call them elsewhere in the program. The problem is that when I call them, they don't seem to be on the same NSMutableArray index... each index contains either the title OR the noType element, when it should be both... can anyone see what I may be doing wrong? Below is the code for the parser. Thanks so much!!
#import "XMLParser.h"
#import "Values.h"
#import "Listing.h"
#import "Master.h"
#implementation XMLParser
#synthesize sharedSingleton, aMaster;
- (XMLParser *) initXMLParser {
[super init];
sharedSingleton = [Values sharedValues];
aMaster = [[Master init] alloc];
return self;
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict {
aMaster = [[Master alloc] init];
//Extract the attribute here.
if ([elementName isEqualToString:#"intro"]) {
aMaster.intro = [attributeDict objectForKey:#"enabled"];
} else if ([elementName isEqualToString:#"item"]) {
aMaster.item_type = [attributeDict objectForKey:#"type"];
//NSLog(#"Did find item with type %#", [attributeDict objectForKey:#"type"]);
//NSLog(#"Reading id value :%#", aMaster.item_type);
} else {
//NSLog(#"No known elements");
}
//NSLog(#"Processing Element: %#", elementName); //HERE
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if(!currentElementValue)
currentElementValue = [[NSMutableString alloc] initWithString:string];
else {
[currentElementValue appendString:string];//[tempString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
CFStringTrimWhitespace((CFMutableStringRef)currentElementValue);
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#"item"]) {
[sharedSingleton.master addObject:aMaster];
NSLog(#"Added %# and %# to the shared singleton", aMaster.title, aMaster.noType); //Only having one at a time added... don't know why
[aMaster release];
aMaster = nil;
} else if ([elementName isEqualToString:#"title"]) {
[aMaster setValue:currentElementValue forKey:#"title"];
} else if ([elementName isEqualToString:#"noType"]) {
[aMaster setValue:currentElementValue forKey:#"noType"];
//NSLog(#"%# should load into the singleton", aMaster.noType);
}
NSLog(#"delimiter");
NSLog(#"%# should load into the singleton", aMaster.title);
NSLog(#"%# should load into the singleton", aMaster.noType);
[currentElementValue release];
currentElementValue = nil;
}
- (void) dealloc {
[aMaster release];
[currentElementValue release];
[super dealloc];
}
#end
Every time that didStartElement is called, you're setting aMaster to a new instance of the Master class. Based on the implementation of didEndElement, it looks like you should only be creating a new instance whenever a new item tag is found. This could be why each entry in the array has one value or the other, since a new instance is created for each value.