Objective c read xml and stock it in array - iphone

I have a problem with a xlm reader. I have some currency rider I'd like to stock in a array but ther's something wrong in my code.
Here is the feed I'd like to read:
http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml
header file
#class Convertisseur;
#interface Convertisseur1ViewController :
UIViewController <UITextFieldDelegate>{
IBOutlet UILabel *usd;
IBOutlet UILabel *euro;
Convertisseur *convertisseur;
// parser XML
NSXMLParser *rssParser;
// elenco degli elementi letti dal feed
NSMutableArray *elencoFeed;
//variabile temporanea pe ogni elemento
NSMutableDictionary *item;
// valori dei campi letti dal feed
NSString *currentElement;
NSMutableString *currentCube;
NSArray *currency;
}
//Dichiarazion del parser
- (void)parseXMLFileAtURL:(NSString *)URL;
#end
Implementation file
#import "Convertisseur1ViewController.h"
#import "Convertisseur.h"
#implementation Convertisseur1ViewController
- (void)parseXMLFileAtURL:(NSString *)URL {
// inizializziamo la lista degli elementi
elencoFeed = [[NSMutableArray alloc] init];
// dobbiamo convertire la stringa "URL" in un elemento "NSURL"
NSURL *xmlURL = [NSURL URLWithString:URL];
// inizializziamo il nostro parser XML
rssParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];
[rssParser setDelegate:self];
// settiamo alcune proprietà
[rssParser setShouldProcessNamespaces:NO];
[rssParser setShouldReportNamespacePrefixes:NO];
[rssParser setShouldResolveExternalEntities:NO];
// avviamo il parsing del feed RSS
[rssParser parse];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
currentElement = [elementName copy];
if ([elementName isEqualToString:#"item"]) {
// inizializza tutti gli elementi
item = [[NSMutableDictionary alloc] init];
currentCube = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if ([elementName isEqualToString:#"Cube"]) {
/* salva tutte le proprietà del feed letto nell'elemento "item", per
poi inserirlo nell'array "elencoFeed" */
[item setObject:currentCube forKey:#"Cube"];
[elencoFeed addObject:[item copy]];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{;
// salva i caratteri per l'elemento corrente
if ([currentElement isEqualToString:#"Cube"]){
[currentCube appendString:string];
}
}
- (void) parserDidEndDocument:(NSXMLParser *)parser {
for(int i=1;i<[elencoFeed count];i++) {
[currency setvalue:[[elencoFeed Objectatindex:i] valueforkey:#"rate"] forkey:[[elencoFeed Objectatindex:i] valueforkey:#"currency"]];
//currency[i]= [elencoFeed Objectatindex:i] valueforkey:#"rate"] forkey:[[elencoFeed Objectatindex:i] valueforkey:#"currency"];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
convertisseur = [[Convertisseur alloc] init];
self.title = #"Convertisseur";
NSString *path = #"http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml";
[self parseXMLFileAtURL:path];
euro.text = currency.text;
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.labelEuro = nil;
self.labelDollar = nil;
self.convertisseur = nil;
}
- (void)dealloc {
[super dealloc];
}

You may consider using TouchXML. I found it a lot easier to use than NSXMLParser
(see http://foobarpig.com/iphone/touchxml-installation-guide.html)
It will give you an in memory representation of your XML document structure, that you will have to walk in order to extract the information you need.

Related

UITableView XML Parser

The data comes smoothly.
But problems have Turkish characters.
Should come as "Düzce" but "ÜZCE" comes.. And,
"Akçakoca" cell in view "çakoca" comes..
XML URL : http://bykrkc.com/test.xml
XMLParser.m;
#import <Foundation/Foundation.h>
#import "XMLParser.h"
#import "XMLFlightList.h"
#implementation XMLParser
#synthesize ucakList = _ucakList;
NSMutableString *currentNodeContent;
NSXMLParser *parser;
XMLFlightList *currentFlight;
bool isStatus;
-(id) loadXMLByUrl:(NSString *)urlString{
_ucakList = [[NSMutableArray alloc] init];
NSError *error;
NSURL *url =[NSURL URLWithString:urlString];
NSString * dataString = [[NSString alloc] initWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
NSData *data = [dataString dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
parser =[[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
[parser parse];
return self;
//[NSString stringWithFormat:#"%#",data];
}
-(void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
currentNodeContent = (NSMutableString *)[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
-(void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
if([elementName isEqualToString:#"sehirler"])
{
currentFlight = [XMLFlightList alloc];
isStatus = YES;
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if(isStatus){
if([elementName isEqualToString:#"ad"]){
currentFlight.name = currentNodeContent;
}
}
if([elementName isEqualToString:#"sehirler"]){
[self.ucakList addObject:currentFlight];
currentFlight =nil;
currentNodeContent = nil;
}
}
#end
XMLParser.h;
#import <Foundation/Foundation.h>
#interface XMLParser : NSObject<NSXMLParserDelegate>
#property (strong,readonly) NSMutableArray *ucakList;
-(id) loadXMLByUrl:(NSString *)urlString;
#end
Result;
2013-12-18 15:49:21.505 tableview01[7663:70b] ÇORUM
2013-12-18 15:49:21.507 tableview01[7663:70b] İSTANBUL
2013-12-18 15:49:21.507 tableview01[7663:70b] ÖMERLİ
2013-12-18 15:49:21.508 tableview01[7663:70b] ÜZCE -> Problem True: DÜZCE
2013-12-18 15:49:21.508 tableview01[7663:70b] çakoca -> Problem True: Akçakoca
2013-12-18 15:49:26.156 tableview01[7663:70b] ÇORUM
Result Images: http://oi42.tinypic.com/2z9mgza.jpg
Your problem in -(void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string method. This method can be called more then once for each tag.
Try change your code:
-(void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if (currentNodeContent == nil) currentNodeContent = [NSMutableString new];
[currentNodeContent appendString:[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
}
As alternative you can use XMLConverter for converting your XML to NSDictionary.
This code :
[XMLConverter convertXMLURL:[NSURL URLWithString:#"http://bykrkc.com/test.xml"] completion:^(BOOL success, NSMutableDictionary *dictionary, NSError *error) {
NSArray *elements = dictionary[#"DocumentElement"][#"sehirler"];
for (NSDictionary *dic in elements) {
NSLog(#"%#", dic[#"ad"]);
}
}];
executed with result:
2013-12-18 18:00:46.037 test[690:70b] ÇORUM
2013-12-18 18:00:46.038 test[690:70b] İSTANBUL
2013-12-18 18:00:46.038 test[690:70b] ÖMERLİ
2013-12-18 18:00:46.039 test[690:70b] DÜZCE
2013-12-18 18:00:46.039 test[690:70b] Akçakoca

NSXMLParser divides strings containing foreign(unicode) characters

I have ran into a peculiar problem with NSXMLParser.
For some reason it cuts out all the characters in front of all the norwegian characters æ, ø and å.
However, the problem seems to be the same with all non a-z characters.(All foreign characters)
Examples:
Reality: Mål
Output: ål
Reality: Le chant des sirènes
Output: ènes
Heres an example from the log where I have printed out the string from:
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
Log:
2012-02-22 14:00:01.647 VotePlayClient[2776:207] found characters: Le chant des sir
2012-02-22 14:00:01.647 VotePlayClient[2776:207] found characters: ènes
You can clearly see that it jumps to a new line whenever it encounters a foreign letter.
I believe that I have to figure out how to append the string or something to that effect.
Here are the NSXMLParser files:
SearchXMLParser.h
#import <Foundation/Foundation.h>
#import "Search.h"
#interface SearchXMLParser : NSObject <NSXMLParserDelegate>
{
NSMutableString *currentNodeContent;
NSMutableArray *searchhits;
NSMutableArray *trackhits;
NSXMLParser *parser;
Search *currentSearch;
}
#property (readonly, retain) NSMutableArray *searchhits;
#property (readonly, retain) NSMutableArray *trackhits;
-(id) loadXMLByURL:(NSString *)urlString;
#end
SearchXMLParser.m
#import "SearchXMLParser.h"
#import "Search.h"
#implementation SearchXMLParser
#synthesize searchhits, trackhits;
-(id) loadXMLByURL:(NSString *)urlString
{
searchhits = [[NSMutableArray alloc] init];
trackhits = [[NSMutableArray alloc] init];
NSURL *url = [NSURL URLWithString:urlString];
NSData *data = [[NSData alloc] initWithContentsOfURL:url];
parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
[parser parse];
return self;
}
- (void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([elementname isEqualToString:#"track"])
{
currentSearch = [Search alloc];
}
if ([elementname isEqualToString:#"track"])
{
currentSearch.trackurl = [attributeDict objectForKey:#"href"];
}
}
- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementname isEqualToString:#"name"])
{
[trackhits addObject:currentNodeContent];
}
if ([elementname isEqualToString:#"track"])
{
currentSearch.track = [trackhits objectAtIndex:0];
currentSearch.artist = [trackhits objectAtIndex:1];
currentSearch.album = [trackhits objectAtIndex:2];
[trackhits removeAllObjects];
[searchhits addObject:currentSearch];
[currentSearch release];
currentSearch = nil;
[currentNodeContent release];
currentNodeContent = nil;
}
}
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
NSLog(#"found characters: %#", string);
currentNodeContent = (NSMutableString *) [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
- (void) dealloc
{
[parser release];
[super dealloc];
}
#end
I have already checked SO for answers and found a couple of similar posts, but nothing that gave a clear solution to this problem.
Can anyone shed some light on this problem? :) Any help is much appreciated!
your parser:foundCharacters: method does not work as it should.
This is from the NSXMLParserDelegate Protocol Reference
The parser object may send the delegate several parser:foundCharacters: messages to report the characters of an element. Because string may be only part of the total character content for the current element, you should append it to the current accumulation of characters until the element changes.
you could try something like this (ARC):
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
NSLog(#"found characters: %#", string);
if (!currentNodeContent) {
currentNodeContent = [[NSMutableString alloc] init];
}
[currentNodeContent appendString:string];
}
- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
// your code here
// when you are done with the string:
currentNodeContent = nil;
}

XML Parsing in iPhone

I am new in xml parsing.I am totaly cofused how many methods we should have to require for xml parsing and what does use of that method.I want to send input to the webservied and by use of xml parsing i want to display result in my text field.
See this questions for resources on how to pick an XML parser for your iOS application:
Choosing the right IOS XML parser
I choose TouchXML for my projects. It is a DOM parser and has XPath support:
https://github.com/TouchCode/TouchXML
You should use the following methods:
- (void)parseXMLFileAtURL:(NSString *)URL { //own method from me, URL could be local file or internet website
itemsOfFeed = [[NSMutableArray alloc] init];
NSURL *xmlURL = [NSURL URLWithString:URL];
feedParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];
[feedParser setDelegate:self];
[feedParser setShouldProcessNamespaces:NO];
[feedParser setShouldReportNamespacePrefixes:NO];
[feedParser setShouldResolveExternalEntities:NO];
[feedParser parse];
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
//in case of an error
}
- (void)parserDidStartDocument:(NSXMLParser *)parser {
// start to parse xml
}
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
feedElement = [elementName copy];
if([elementName isEqualToString:#"item"]) { //main xml tag
item = [[NSMutableDictionary alloc] init];
feedTitle = [[NSMutableString alloc] init];
feedDate = [[NSMutableString alloc] init];
feedText = [[NSMutableString alloc] init];
feedLink = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if([feedElement isEqualToString:#"title"]) {
[feedTitle appendString:string];
} else if([feedElement isEqualToString:#"link"]) { // some examples of tags
[feedLink appendString:string];
} else if([feedElement isEqualToString:#"content:encoded"]) {
[feedText appendString:string];
} else if([feedElement isEqualToString:#"pubDate"]) {
[feedDate appendString:string];
}
}
- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if([elementName isEqualToString:#"item"]) {
[item setObject:feedTitle forKey:#"title"];
[item setObject:feedDate forKey:#"date"];
[item setObject:feedText forKey:#"text"];
[item setObject:feedLink forKey:#"link"];
[itemsOfFeed addObject:item];
}
}
- (void)parserDidEndDocument:(NSXMLParser *)parser {
[self.myTableView reloadData]; // for example reload table view
[self writeArrayToFile]; //or write to a local property list
}
in your header file:
NSMutableArray *itemsOfFeed;
NSXMLParser *feedParser;
NSMutableDictionary *item;
NSMutableString *feedElement;
NSMutableString *feedTitle, feedDate, feedText, feedLink; //strings of your tags
Then you have:
NSArray
NSDictionary
object a
object b
object c
Just access 'object a' and put it in your text field
hopefully this code example helps you
Here is the very helpful tutorial.
http://www.edumobile.org/iphone/iphone-programming-tutorials/parsing-an-xml-file/
Hi I would personally prefer to use NSXMLParser.
Write your own code which uses NSXMLParserDelegate methods implementation.
But still there are some third party libraries available. Here is a nice example to compare how all those parser differs from each other and also having nice explanation. Code is also available over there.
Hope this will be helpful to you.
-Mrunal
i have a demo app for parsing static XML to UITableView, I think this one will surely will help you.
Availiable third party libraries available below, depend on developer choice this, which is best?
NSXMLParser
libxml2
TBXMLParser
TouchXMLParser
KissXMLParser
TinyXMLParser
GDataXMLParser
And you can also get more information from this great tutorial link
http://www.raywenderlich.com/553/how-to-chose-the-best-xml-parser-for-your-iphone-project
Try this:
XMLReader.h
//
// XMLReader.h
//
//
#import <Foundation/Foundation.h>
#interface XMLReader : NSObject <NSXMLParserDelegate>
{
NSMutableArray *dictionaryStack;
NSMutableString *textInProgress;
NSError *errorPointer;
}
+ (NSDictionary *)dictionaryForPath:(NSString *)path error:(NSError **)errorPointer;
+ (NSDictionary *)dictionaryForXMLData:(NSData *)data error:(NSError **)errorPointer;
+ (NSDictionary *)dictionaryForXMLString:(NSString *)string error:(NSError **)errorPointer;
#end
XMLReader.m
//
// XMLReader.m
//
#import "XMLReader.h"
//NSString *const kXMLReaderTextNodeKey = #"text";
#interface XMLReader (Internal)
- (id)initWithError:(NSError **)error;
- (NSDictionary *)objectWithData:(NSData *)data;
#end
#implementation XMLReader
#pragma mark -
#pragma mark Public methods
+ (NSDictionary *)dictionaryForPath:(NSString *)path error:(NSError **)errorPointer
{
NSString *fullpath = [[NSBundle bundleForClass:self] pathForResource:path ofType:#"xml"];
NSData *data = [[NSFileManager defaultManager] contentsAtPath:fullpath];
NSDictionary *rootDictionary = [XMLReader dictionaryForXMLData:data error:errorPointer];
return rootDictionary;
}
+ (NSDictionary *)dictionaryForXMLData:(NSData *)data error:(NSError **)error
{
XMLReader *reader = [[XMLReader alloc] initWithError:error];
NSDictionary *rootDictionary = [reader objectWithData:data];
[reader release];
return rootDictionary;
}
+ (NSDictionary *)dictionaryForXMLString:(NSString *)string error:(NSError **)error
{
NSArray* lines = [string componentsSeparatedByString:#"\n"];
NSMutableString* strData = [NSMutableString stringWithString:#""];
for (int i = 0; i < [lines count]; i++)
{
[strData appendString:[[lines objectAtIndex:i] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
}
NSData *data = [strData dataUsingEncoding:NSUTF8StringEncoding];
return [XMLReader dictionaryForXMLData:data error:error];
}
#pragma mark -
#pragma mark Parsing
- (id)initWithError:(NSError **)error
{
if ((self = [super init]))
{
errorPointer = *error;
}
return self;
}
- (void)dealloc
{
[dictionaryStack release];
[textInProgress release];
[super dealloc];
}
- (NSDictionary *)objectWithData:(NSData *)data
{
// Clear out any old data
[dictionaryStack release];
[textInProgress release];
dictionaryStack = [[NSMutableArray alloc] init];
textInProgress = [[NSMutableString alloc] init];
// Initialize the stack with a fresh dictionary
[dictionaryStack addObject:[NSMutableDictionary dictionary]];
// Parse the XML
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
BOOL success = [parser parse];
[parser release];
// Return the stack's root dictionary on success
if (success){
NSDictionary *resultDict = [dictionaryStack objectAtIndex:0];
return resultDict;
}
return nil;
}
# pragma mark
# pragma mark - NSXMLParserDelegate methods
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
// Get the dictionary for the current level in the stack
NSMutableDictionary *parentDict = [dictionaryStack lastObject];
// Create the child dictionary for the new element
NSMutableDictionary *childDict = [NSMutableDictionary dictionary];
// Initialize child dictionary with the attributes, prefixed with '#'
for (NSString *key in attributeDict) {
[childDict setValue:[attributeDict objectForKey:key]
forKey:key];
}
// If there's already an item for this key, it means we need to create an array
id existingValue = [parentDict objectForKey:elementName];
if (existingValue){
NSMutableArray *array = nil;
if ([existingValue isKindOfClass:[NSMutableArray class]]){
// The array exists, so use it
array = (NSMutableArray *) existingValue;
}
else{
// Create an array if it doesn't exist
array = [NSMutableArray array];
[array addObject:existingValue];
// Replace the child dictionary with an array of children dictionaries
[parentDict setObject:array forKey:elementName];
}
// Add the new child dictionary to the array
[array addObject:childDict];
}
else{
// No existing value, so update the dictionary
[parentDict setObject:childDict forKey:elementName];
}
// Update the stack
[dictionaryStack addObject:childDict];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
// Update the parent dict with text info
NSMutableDictionary *dictInProgress = [dictionaryStack lastObject];
// Pop the current dict
[dictionaryStack removeLastObject];
// Set the text property
if ([textInProgress length] > 0){
if ([dictInProgress count] > 0){
[dictInProgress setObject:textInProgress forKey:kXMLReaderTextNodeKey];
}
else{
// Given that there will only ever be a single value in this dictionary, let's replace the dictionary with a simple string.
NSMutableDictionary *parentDict = [dictionaryStack lastObject];
id parentObject = [parentDict objectForKey:elementName];
// Parent is an Array
if ([parentObject isKindOfClass:[NSArray class]]){
[parentObject removeLastObject];
[parentObject addObject:textInProgress];
}
// Parent is a Dictionary
else{
[parentDict removeObjectForKey:elementName];
[parentDict setObject:textInProgress forKey:elementName];
}
}
// Reset the text
[textInProgress release];
textInProgress = [[NSMutableString alloc] init];
}
// If there was no value for the tag, and no attribute, then remove it from the dictionary.
else if ([dictInProgress count] == 0){
NSMutableDictionary *parentDict = [dictionaryStack lastObject];
[parentDict removeObjectForKey:elementName];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
// Build the text value
[textInProgress appendString:[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
// Set the error pointer to the parser's error object
if (errorPointer)
errorPointer = parseError;
}
#end
Use it:
NSMutableDictionary *yourDic= (NSMutableDictionary *)[XMLReader dictionaryForXMLData:yourData error:&error];

XML parser SDK for IOS

I need to be able to parse XML for a project I'm working on.
How can I parse XML from a web page to the iPhone then read its contents?
Follow this , how to parse XML in Objective c using ASIHTTPRequest and handle all this methods
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
See this link too
NSString *url=#"http://www.lancers.jp/work";
NSData *data=[NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
NSDictionary *dict=[XMLParser dictionaryForXMLData:data error:nil];
NSLog(#"%#",[dict description]);
and use below file::::::
XMLParser.h
//
// XMLReader.h
//
// Created by Troy on 9/18/10.
// Copyright 2010 Troy Brant. All rights reserved.
//
#import <Foundation/Foundation.h>
#interface XMLParser : NSObject<NSXMLParserDelegate>
{
NSMutableArray *dictionaryStack;
NSMutableString *textInProgress;
NSError **errorPointer;
}
+ (NSDictionary *)dictionaryForXMLData:(NSData *)data error:(NSError **)errorPointer;
+ (NSDictionary *)dictionaryForXMLString:(NSString *)string error:(NSError **)errorPointer;
#end
XMLParser.m
//
// XMLReader.m
//
// Created by Troy on 9/18/10.
// Copyright 2010 Troy Brant. All rights reserved.
//
#import "XMLParser.h"
NSString *const kXMLReaderTextNodeKey = #"text";
#interface XMLParser (Internal)
- (id)initWithError:(NSError **)error;
- (NSDictionary *)objectWithData:(NSData *)data;
#end
//NSString *url=[NSString stringWithFormat:#"%#",NSLocalizedString(#"locationname", nil)];
//url=[NSString stringWithFormat:url,app.latnear,app.lngnear];
//NSData *data=[NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
//NSDictionary *dict=[XMLReader dictionaryForXMLData:data error:nil];
#implementation XMLParser
#pragma mark -
#pragma mark Public methods
+ (NSDictionary *)dictionaryForXMLData:(NSData *)data error:(NSError **)error
{
XMLParser *reader = [[XMLParser alloc] initWithError:error];
NSDictionary *rootDictionary = [reader objectWithData:data];
[reader release];
return rootDictionary;
}
+ (NSDictionary *)dictionaryForXMLString:(NSString *)string error:(NSError **)error
{
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
return [XMLParser dictionaryForXMLData:data error:error];
}
#pragma mark -
#pragma mark Parsing
- (id)initWithError:(NSError **)error
{
if (self = [super init])
{
errorPointer = error;
}
return self;
}
- (void)dealloc
{
[dictionaryStack release];
[textInProgress release];
[super dealloc];
}
- (NSDictionary *)objectWithData:(NSData *)data
{
// Clear out any old data
[dictionaryStack release];
[textInProgress release];
dictionaryStack = [[NSMutableArray alloc] init];
textInProgress = [[NSMutableString alloc] init];
// Initialize the stack with a fresh dictionary
[dictionaryStack addObject:[NSMutableDictionary dictionary]];
// Parse the XML
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
BOOL success = [parser parse];
// Return the stack's root dictionary on success
if (success)
{
NSDictionary *resultDict = [dictionaryStack objectAtIndex:0];
return resultDict;
}
return nil;
}
#pragma mark -
#pragma mark NSXMLParserDelegate methods
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
// Get the dictionary for the current level in the stack
NSMutableDictionary *parentDict = [dictionaryStack lastObject];
// Create the child dictionary for the new element, and initilaize it with the attributes
NSMutableDictionary *childDict = [NSMutableDictionary dictionary];
[childDict addEntriesFromDictionary:attributeDict];
// If there's already an item for this key, it means we need to create an array
id existingValue = [parentDict objectForKey:elementName];
if (existingValue)
{
NSMutableArray *array = nil;
if ([existingValue isKindOfClass:[NSMutableArray class]])
{
// The array exists, so use it
array = (NSMutableArray *) existingValue;
}
else
{
// Create an array if it doesn't exist
array = [NSMutableArray array];
[array addObject:existingValue];
// Replace the child dictionary with an array of children dictionaries
[parentDict setObject:array forKey:elementName];
}
// Add the new child dictionary to the array
[array addObject:childDict];
}
else
{
// No existing value, so update the dictionary
[parentDict setObject:childDict forKey:elementName];
}
// Update the stack
[dictionaryStack addObject:childDict];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
// Update the parent dict with text info
NSMutableDictionary *dictInProgress = [dictionaryStack lastObject];
// Set the text property
if ([textInProgress length] > 0)
{
[dictInProgress setObject:textInProgress forKey:kXMLReaderTextNodeKey];
// Reset the text
[textInProgress release];
textInProgress = [[NSMutableString alloc] init];
}
// Pop the current dict
[dictionaryStack removeLastObject];
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
// Build the text value
[textInProgress appendString:string];
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
// Set the error pointer to the parser's error object
*errorPointer = parseError;
}
#end

Iphone NSXMLParser NSCFString memory leak

I am building an app that parses an rss feed. In the app there are two different types of feeds with different names for the elements in the feed, so I have created an NSXMLParser NSObject that takes the name of the elements of each feed before parsing. Here is my code:
NewsFeedParser.h
#import
#interface NewsFeedParser : NSObject {
NSInteger NewsSelectedCategory;
NSXMLParser *NSXMLNewsParser;
NSMutableArray *newsCategories;
NSMutableDictionary *NewsItem;
NSMutableString *NewsCurrentElement, *NewsCurrentElement1, *NewsCurrentElement2, *NewsCurrentElement3;
NSString *NewsItemType, *NewsElement1, *NewsElement2, *NewsElement3;
NSInteger NewsNumElements;
}
- (void) parseXMLFileAtURL:(NSString *)URL;
#property(nonatomic, retain) NSString *NewsItemType;
#property(nonatomic, retain) NSString *NewsElement1;
#property(nonatomic, retain) NSString *NewsElement2;
#property(nonatomic, retain) NSString *NewsElement3;
#property(nonatomic, retain) NSMutableArray *newsCategories;
#property(assign, nonatomic) NSInteger NewsNumElements;
#end
NewsFeedParser.m
#import "NewsFeedParser.h"
#implementation NewsFeedParser
#synthesize NewsItemType;
#synthesize NewsElement1;
#synthesize NewsElement2;
#synthesize NewsElement3;
#synthesize newsCategories;
#synthesize NewsNumElements;
- (void)parserDidStartDocument:(NSXMLParser *)parser{
}
- (void)parseXMLFileAtURL:(NSString *)URL
{
newsCategories = [[NSMutableArray alloc] init];
URL = [URL stringByReplacingOccurrencesOfString:#" " withString:#""];
URL = [URL stringByReplacingOccurrencesOfString:#"\n" withString:#""];
URL = [URL stringByReplacingOccurrencesOfString:#" " withString:#""];
//you must then convert the path to a proper NSURL or it won't work
NSURL *xmlURL = [NSURL URLWithString:URL];
// here, for some reason you have to use NSClassFromString when trying to alloc NSXMLParser, otherwise you will get an object not found error
// this may be necessary only for the toolchain
[[NSURLCache sharedURLCache] setMemoryCapacity:0];
[[NSURLCache sharedURLCache] setDiskCapacity:0];
NSXMLNewsParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];
// Set self as the delegate of the parser so that it will receive the parser delegate methods callbacks.
[NSXMLNewsParser setDelegate:self];
// Depending on the XML document you're parsing, you may want to enable these features of NSXMLParser.
[NSXMLNewsParser setShouldProcessNamespaces:NO];
[NSXMLNewsParser setShouldReportNamespacePrefixes:NO];
[NSXMLNewsParser setShouldResolveExternalEntities:NO];
[NSXMLNewsParser parse];
[NSXMLNewsParser release];
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
NSString * errorString = [NSString stringWithFormat:#"Unable to download story feed from web site (Error code %i )", [parseError code]];
NSLog(#"error parsing XML: %#", errorString);
UIAlertView * errorAlert = [[UIAlertView alloc] initWithTitle:#"Error loading content" message:errorString delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[errorAlert show];
[errorAlert release];
[errorString release];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
NewsCurrentElement = [elementName copy];
if ([elementName isEqualToString:NewsItemType])
{
// clear out our story item caches...
NewsItem = [[NSMutableDictionary alloc] init];
NewsCurrentElement1 = [[NSMutableString alloc] init];
NewsCurrentElement2 = [[NSMutableString alloc] init];
if(NewsNumElements == 3)
{
NewsCurrentElement3 = [[NSMutableString alloc] init];
}
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if ([elementName isEqualToString:NewsItemType])
{
// save values to an item, then store that item into the array...
[NewsItem setObject:NewsCurrentElement1 forKey:NewsElement1];
[NewsItem setObject:NewsCurrentElement2 forKey:NewsElement2];
if(NewsNumElements == 3)
{
[NewsItem setObject:NewsCurrentElement3 forKey:NewsElement3];
}
[newsCategories addObject:[[NewsItem copy] autorelease]];
[NewsCurrentElement release];
[NewsCurrentElement1 release];
[NewsCurrentElement2 release];
if(NewsNumElements == 3)
{
[NewsCurrentElement3 release];
}
[NewsItem release];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
//NSLog(#"found characters: %#", string);
// save the characters for the current item...
if ([NewsCurrentElement isEqualToString:NewsElement1]) {
[NewsCurrentElement1 appendString:string];
} else if ([NewsCurrentElement isEqualToString:NewsElement2]) {
[NewsCurrentElement2 appendString:string];
} else if (NewsNumElements == 3 && [NewsCurrentElement isEqualToString:NewsElement3])
{
[NewsCurrentElement3 appendString:string];
}
}
- (void)dealloc {
[super dealloc];
[newsCategories release];
[NewsItemType release];
[NewsElement1 release];
[NewsElement2 release];
[NewsElement3 release];
}
When I create an instance of the class I do like so:
NewsFeedParser *categoriesParser = [[NewsFeedParser alloc] init];
if(newsCat == 0)
{
categoriesParser.NewsItemType = #"article";
categoriesParser.NewsElement1 = #"category";
categoriesParser.NewsElement2 = #"catid";
}
else
{
categoriesParser.NewsItemType = #"article";
categoriesParser.NewsElement1 = #"category";
categoriesParser.NewsElement2 = #"feedUrl";
}
[categoriesParser parseXMLFileAtURL:feedUrl];
newsCategories = [[NSMutableArray alloc] initWithArray:categoriesParser.newsCategories copyItems:YES];
[self.tableView reloadData];
[categoriesParser release];
If I run the app with the leaks instrument, the leaks point to the [NSXMLNewsParser parse] call in the NewsFeedParser.m.
Here is a screen shot of the Leaks instrument with the NSCFStrings leaking:
http://img139.imageshack.us/img139/3997/leaks.png
For the life of me I can't figure out where these leaks are coming from. Any help would be greatly appreciated.
The leak occurred in the didStartElement method. I was copying elementName without releasing it.
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
NewsCurrentElement = [[elementName copy] autorelease];
if ([elementName isEqualToString:NewsItemType])
{
// clear out our story item caches...
NewsItem = [[NSMutableDictionary alloc] init];
NewsCurrentElement1 = [[NSMutableString alloc] init];
NewsCurrentElement2 = [[NSMutableString alloc] init];
if(NewsNumElements == 3)
{
NewsCurrentElement3 = [[NSMutableString alloc] init];
}
}
}
You might also want to release (if necessary) the allocated NSMutableString properties before allocating another NSMutableString into the property like so:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
if (NewsCurrentElement) {
[NewsCurrentElement release], NewsCurrentElement = nil;
}
NewsCurrentElement = [[elementName copy] autorelease];
if ([elementName isEqualToString:NewsItemType]) {
// clear out our story item caches...
if (NewsItem) {
[NewsItem release], NewsItem = nil;
}
NewsItem = [[NSMutableDictionary alloc] init];
if (NewsCurrentElement1) {
[NewsCurrentElement1 release], NewsCurrentElement1 = nil;
}
NewsCurrentElement1 = [[NSMutableString alloc] init];
if (NewsCurrentElement2) {
[NewsCurrentElement2 release], NewsCurrentElement2 = nil;
}
NewsCurrentElement2 = [[NSMutableString alloc] init];
if(NewsNumElements == 3) {
if (NewsCurrentElement3) {
[NewsCurrentElement3 release], NewsCurrentElement3 = nil;
}
NewsCurrentElement3 = [[NSMutableString alloc] init];
}
}
}