Clark Kent
Clark Kent

Reputation: 11

iPhone XML parser wont add object to mutable array

Greetings,

I have a problem with adding an object from parser to mutable array. It was working fine until I moved MtableArray from AppDelegate to ViewController. This is where I call parser (MutableArray is inside this View also):

 NSURL *url = [[NSURL alloc] initWithString:@"http://example.com"];
 NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];

 parser = [[XMLParser alloc] init];

 [xmlParser setDelegate:parser];

 [xmlParser parse];

...and this is inside parser where objects should be added:

if([elementName isEqualToString:@"Element"]) {

  [viewController.marray addObject:parsedObj];

  [parsedObj release];
  parsedObj = nil;
  }

marray is synthesized inside viewController. Parser is doing good job, I tried with NSLog, but marray.count is always (null). Please help!!!

Upvotes: 1

Views: 910

Answers (5)

Clark Kent
Clark Kent

Reputation: 1

I tried a different approach and now I have two solutions and a problem with both of them. I have a XML parser that should add objects to NSMutableArray from where it has been called at first. Parser is working OK, but here is the problem. First approach:

I changed NSXMLParser's init method inside my XMLParser.m (mArray is for Mutable Array and):

- (XMLParser *) initXMLParser:(id)sender {
 [super init];
 mArray=(NSMutableArray *)sender; 
 return self;}

mArray in implementation file XMLParser.h:

@interface XMLParser : NSObject <NSXMLParserDelegate>{
 NSMutableString *currentElementValue; 
 NSMutableArray *mArray;
 Object *aObject;}
-(XMLParser *) initXMLParser:(id)sender;
@end

So, let's get to the part where we call XMLParser from ViewController.m:

    mArray = [[NSMutableArray alloc] init];
 NSURL *url = [[NSURL alloc] initWithString:@"http://www.a.com/some.xml"];
 NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
 XMLParser *parser = [[XMLParser alloc] initXMLParser:self.mArray];
 [xmlParser setDelegate:parser];
    [xmlParser parse];

...and here is ViewController.h:

    @class XMLParser;
    @interface AccomodationSecondViewController : UIViewController{ 
    @public NSMutableArray *mArray;}
    @property (nonatomic, retain) NSMutableArray *mArray;

I'm not sure because this is first time I'm using public objects so... Anyway, this is the part inside XMLParser.m that should add objects:

    //This is for closing bracket
    if([elementName isEqualToString:@"Element"]) {    
     [mArray addObject:aObject];
     [aObject release];
     aObject=nil;}

So, the idea is to make mutable array mArray public inside ViewController and to send pointer to it to XMLParser. I know that this may be little unnatural, but I just want to get it to work.

My other idea is this: To send pointer of ViewController to XMLParser and do the rest. So, I changed initXMLParser:

- (XMLParser *) initXMLParser:(id)sender {
    [super init];
    viewController=(ViewController *)sender;    
    return self;}

and this is the part inside ViewController.m where I call my method:

XMLParser *parser = [[XMLParser alloc] initXMLParser:self];

then I add object:

[viewController.mArray addObject:aObject];

Why is this not working??!!

Upvotes: 0

Adam Milligan
Adam Milligan

Reputation: 2826

Have you verified that the marray property is non-nil? If it somehow hasn't been set properly then all of the insertions will be no-ops, and the the result of the count method will be nil.

Now that you've posted more code, this line is your problem:

[marray init];

You need to alloc/init a new NSMutableArray; this line is simply sending the init message to nil.

Upvotes: 1

Clark Kent
Clark Kent

Reputation: 11

Here is some more code: ViewController.h

#import <UIKit/UIKit.h>

@class XMLParser;

@interface ViewController : UIViewController{

    NSMutableArray *marray;

    XMLParser *parser;
}

@property (nonatomic, retain) NSMutableArray *marray;
@property (nonatomic, retain) XMLParser *parser;

@end

ViewController.m

#

import "ViewController.h"
    #import "ParsedObj.h"
    #import "XMLParser.h"

    @synthesize marray;
    @synthesize parser;

(...)

    -(void)search:(id)sender{

        [marray init];

         NSURL *url = [[NSURL alloc] initWithString:@"http://example.com"];


    NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];

     parser = [[XMLParser alloc] init];

     [xmlParser setDelegate:parser];

     [xmlParser parse];

(...)

    @end

XMLparser.h:

#import <UIKit/UIKit.h>

@class ViewController, ParsedObj;

@interface XMLParser : NSObject <NSXMLParserDelegate>{

    NSMutableString *currentElementValue;

    ViewController *viewController;
    ParsedObj *parsedObj;

}

@end

..and XMLparser.m:

#import "XMLParser.h"
#import "ViewController.h"
#import "ParsedObj.h"

@implementation XMLParser

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName 
  namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName 
    attributes:(NSDictionary *)attributeDict {

    if([elementName isEqualToString:@"Root"]) {
        //Initialize the array.
        viewController.marray = [[NSMutableArray alloc] init];
    }
    else if([elementName isEqualToString:@"Element"]) {

        //Initialize the hotel.
        parsedObj = [[ParsedObj alloc] init];

        //Extract the attribute here.
        parsedObj.ID = [[attributeDict objectForKey:@"id"] integerValue];
    }
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string { 

    if(!currentElementValue) 
        currentElementValue = [[NSMutableString alloc] initWithString:string];
    else
        [currentElementValue appendString:string];
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName 
  namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {

    if([elementName isEqualToString:@"Root"])
        return;

    //There is nothing to do if we encounter the Books element here.
    //If we encounter the Book element howevere, we want to add the book object to the array
    // and release the object.
    if([elementName isEqualToString:@"Element"]) {

        [accomodationController.marray addObject:parsedObj];

        parsedObj = nil;
    }
    else {
        NSString *cValue=[currentElementValue stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
        [parsedObj setValue:cValue forKey:elementName];
    }

    [currentElementValue release];
    currentElementValue = nil;
}



- (void)dealloc {
    [parsedObj release];
    [currentElementValue release];
    [super dealloc];
}


@end

Upvotes: 0

KingofBliss
KingofBliss

Reputation: 15115

Try like this,

[viewController.marray addObject:[parsedObj copy]];

Upvotes: 1

squeezemylime
squeezemylime

Reputation: 2102

Can you post more code?

If you are inside view controller when marray is being called, you shouldn't have to call viewController.marray, just marray

One suggestion, from my experience in parsing, is to use an NSMutableDictionary instead of an array..so, for instance:

    - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{

    currentElement = [elementName copy];

if([elementName isEqualToString:@"item"])
{
    //Clear out story item caches
    item = [[NSMutableDictionary alloc] init];
    currentTitle = [[NSMutableString alloc] init];
    currentAddress = [[NSMutableString alloc] init];
    currentCity = [[NSMutableString alloc] init];
    currentState = [[NSMutableString alloc] init];
    currentZip = [[NSMutableString alloc] init];
    currentId = [[NSMutableString alloc] init];
}

}

and then to add everything:

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{

    if([elementName isEqualToString:@"item"])
{
    //Save values to an item, then store that item into the array
    [item setObject:currentTitle forKey:@"title"];
    [item setObject:currentAddress forKey:@"address"];
    [item setObject:currentCity forKey:@"city"];
    [item setObject:currentState forKey:@"state"];
    [item setObject:currentZip forKey:@"zip"];
    [item setObject:currentId forKey:@"id"];

           // venues is the mutable array
    [venues addObject:[item copy]];
    } else {
        return;
    }
}

Now my mutable array has all the elements I need, and I can do various things with it (like reload a table cell). The above code has been tested and verified as working.

Upvotes: 0

Related Questions