I've parsed an xml and stored the parsed data on strings which are property declared, retained and synthesized, the values are getting into these strings during parsing, but are being invalidated after the parser finish parsing. I'm not able to access them from another IBAction method, where i get EXEC BAD ACCESS error, which, from my experiences, think that arises when the debugger is accessing an invalidated memory.
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
//NSLog(#"found this element: %#", elementName);
currentElement = [elementName copy];
if ([elementName isEqualToString:#"resp"]) {
// clear out our story item caches...
}
else if ([currentElement isEqualToString:#"org"]) {
name = [attributeDict objectForKey:#"fileid"];
extention = [attributeDict objectForKey:#"extension"];
NSLog(#"image named %# of type %#",name,extention);
}
else if ([currentElement isEqualToString:#"photo"]) {
photoid = [attributeDict objectForKey:#"photoid"];
NSLog (#"photo id = %#",photoid);
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
//NSLog(#"found characters: %#", string);
// save the characters for the current item...
if ([currentElement isEqualToString:#"userid"]) {
userid =[NSString stringWithString:string];
NSLog (#"user id = %#",userid);
}
else if ([currentElement isEqualToString:#"albumid"]) {
albumid =[NSString stringWithString:string];
NSLog (#"album id = %#",albumid);
}
}
This portion is working fine, but
- (IBAction)download {
NSString *urlstr = [NSString stringWithFormat:#"http://staging-data.mysite.com/%#/albumphoto/%#/%#/%#.%#",userid,albumid,photoid,name,extention];
NSLog(urlstr);
NSURL *url= [NSURL URLWithString:urlstr];
NSData *imgdata = [[NSData alloc]initWithContentsOfURL:url];
UIImage *img = [UIImage imageWithData:imgdata];
newImage.image=img;
[imgdata release];
}
This results in error. pls help
I suggest you look carefully at this line:
name = [attributeDict objectForKey:#"fileid"];
Which sets an attribute to an NSString owned by the XML parser without copying it, retaining it etc. I suggest you take a copy, and retain that.
My concern is that when the XML parser goes away, it takes the name string with it, along with extension and photoid.
You have to implement this method also to know when an end tag is encountered.
(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
Related
I have already read about NSxmlparser. I have the following file, but I do not understand how I should do for the parser.
i Trying to parse xml list without parent node using NSxmlparser
<nodes1>
<child1>txt1</child1>
<child2>Txt2</child2>
</nodes1>
<nodes1>
<child1>Txt3</child1>
<child2>Txt4</child2>
</nodes1>
<nodes1>
<child1>Txt5</child1>
<child2>Txt6</child2>
</nodes1>
Get the file and start parsing
NSError *error;
NSString *xmlPath = [[NSBundle mainBundle] pathForResource:#"yourFile" ofType:#"xml"];
NSString* contents = [NSString stringWithContentsOfFile:xmlPath encoding:NSUTF8StringEncoding error:&error];
self.xmlData = [[NSData alloc] init];
self.xmlData = [contents dataUsingEncoding:NSUTF8StringEncoding];
self.xmlParser = [[NSXMLParser alloc] initWithData:self.xmlData];
[self.xmlParser setDelegate: self];
[self.xmlParser setShouldResolveExternalEntities: YES];
[self.xmlParser parse];
then for parsing
BOOL isValid_child1 = NO;
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *) namespaceURI qualifiedName:(NSString *)qName attributes: (NSDictionary *)attributeDict
{
if ([elementName isEqualToString:#"child1"]) {
isValid_child1 = YES;
}
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if (isValid_child1) {
valueInChild1 = string; // string is txt1
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:#"child1"]) {
isValid_child1 = NO;
[array addObject:valueInChild1]; // to add value for child1 to an array
}
I know this is dirty ;) but it works, you can do it more flexible.
When i retrieve the XML file from URL, i can Parse all the elements from the XML file and view in log file, my parsing code looks like this,
-(id) loadXMLByURL:(NSString *)urlString{
tweets = [[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:#"Products"]){
currentTweet = [egsBase alloc];}}
-(void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if([elementName isEqualToString:#"img"]){
currentTweet.img=[UIImage imageWithData: [NSData dataWithContentsOfURL: [NSURL URLWithString:currentNodeContent]]];}
if ([elementName isEqualToString:#"Products"]) {
[tweets addObject:currentTweet.img];
currentTweet = nil;
currentNodeContent = nil;}}
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
currentNodeContent = (NSMutableString *) [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];}
I use the class name for parsing as XmlParsing . In the controller class i had implement the class file XmlParsing as XmlParsing xmlparser; Now in viewDidLoad() function i use code like this,
- (void)viewDidLoad{
[super viewDidLoad];
xmlParser = [[XMLParsing alloc] loadXMLByURL:#"http://images.com/Products.xml"];
UIImage *currentImage = [[xmlParser tweets] objectAtIndex:0];
customimage.image = currentImage;}
Here the customimage is reference for UIImageView, Now the problem is i can get only the last element value and show in the UIImageView connected in story board (ie) for better understanding it shows only the last element image and not the previous elements i use in elements. how to solve this ? where am doing wrong. Kindly suggest me.
STRUCTURE OF XML
<Products>
<products id="1">
<img>http://images.com/images/Main_pic.png</img>
</products>
<products id="2">
<img>http://images.com/images/image1.png</img>
</products>
</Products>
My NSLog shows for parsing XML file,
images <UIImage: 0x71529a0>
images <UIImage: 0x74a6750>
When i print to check what image i get it shows the current image as,
Current,<UIImage: 0x74a6750>
here you can see the current image shows the last element alone not the previous one
- (void)viewDidLoad
{
1) [super viewDidLoad];
2) xmlParser = [[XMLParsing alloc] loadXMLByURL:#"http://images.com/Products.xml"];
3) UIImage currentImage = [[xmlParser tweets] objectAtIndex:0];
4) customimage.image = currentImage;
}
I have added line numbers for ease of understanding :
Line number 3 : You always get a new image (Am i right?)
Line number 4 : whenever u get a new image you set that image to the same
imageview(customimage) due to this previous images gets replaced
with the new one.
Ideally you should set every new image to respective image views.
If u r trying to load these images in a table view ---
Then name of image view remains same but actually the object is different so you should access the imageview of each cell by its TAG assigned in cellForRowAtIndexPath.
Updated Code
-(void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if([elementName isEqualToString:#"products"])
{
currentTweet = [egsBase alloc];}}
}
-(void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if([elementName isEqualToString:#"img"])
{
currentTweet.img=[UIImage imageWithData: [NSData dataWithContentsOfURL: [NSURL URLWithString:currentNodeContent]]];
[tweets addObject:currentTweet.img];
}
if ([elementName isEqualToString:#"products"])
{
currentTweet = nil;
currentNodeContent = nil;
}
}
Desc : while parsing when "products" is found as start element and object gets created, and when end element is found image gets associated to objects and gets added to array.
I see the error is : you are using "Products" in parsing condition instead of "products"
Hope the idea helps
:)
Append the string to currentnodecontent
if(!currentNodeContent )
currentNodeContent = [[NSMutableString alloc] initWithString:string];
else
[currentNodeContent appendString:string];
How do I parse this type of XML using NSXMLParser
<category> <categoryName> userName</categoryName> <categoryName>password</categoryName><category>
Declare array and a string in h file like:
NSMutableArray *aryCategory;
NSString *strCategroyName;
in .m file for starting Parser use:
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:yourData]; // your data will be instance of NSData or NSMutableData
parser.delegate = self;
[parser parse];
this will be done once you get your xml data. For handling result you can use NSXMLParserDelegate as follows:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
if ([elementName isEqualToString:#"category"]) {
aryCategory = [[NSMutableArray alloc] init];
strCategroyName = #"";
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
strCategroyName = [NSString stringWithFormat:#"%#%#", strCategroyName, string];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#"categoryName"]) {
[aryCategory addObject:strCategroyName];
strCategroyName = #"";
}
}
Now you will have your array fill with all of your category name.
Hope this helps :)
Write the <category> in didStart and didEnd elements which are the parser's delegates.
At the moment i've got the following code to get data from an xml file. This works so far
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict{
currentElement = [elementName copy];
if ([elementName isEqualToString:#"Placemark"]) {
placemarkData = [[NSMutableDictionary alloc] init];
currentTitle = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
if ([currentElement isEqualToString:#"name"])
[placemarkData addObject:string forKey:currentElement];
if ([currentElement isEqualToString:#"address"])
[placemarkData addObject:string forKey:currentElement];
if ([currentElement isEqualToString:#"coordinates"])
[placemarkData addObject:string forKey:currentElement];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName{
if ([elementName isEqualToString:#"Placemark"]) {
[Placemarks addObject:[placemarkData copy]];
NSString *batsen = [placemarkData objectForKey:#"name"];
NSLog(#"adding story: %#", batsen);
}
}
This dictionary will be loaded into a array. Problem is that where this array got loaded in need to find the right entry in the array. I do it like this
TabbedCalculationAppDelegate *appDelegate = (TabbedCalculationAppDelegate *)[[UIApplication sharedApplication] delegate];
NSArray *jean = appDelegate.Placemarks;
NSDictionary *boekje = [jean objectAtIndex:1];
NSString *coordinaten = [boekje objectForKey:#"coordinates"];
NSString *naam = [boekje objectForKey:#"name"];
NSLog(#"poep: %#" , naam);
NSLog(#"wwhoppa: %#", coordinaten);
titel.text = coordinaten;
But since arrays works with index I can't find a way to get the right entry. To clarify, a user clicks in a mapview on an annotation. This annotation then gets the corresponding detailview. The detailview can check which annotation is clicked by the view.annotation.title;
So I actually need a dictionary in a dictionary with name as key in the root dictionary. Is this possible? and on what way I can implement this?
Is this a little bit clear? Else just ask!
Thnx guys
Yes, this is a good idea. Just pass the 'inner' dictionary as the object parameter to setObject:forKey: on the 'outer' dictionary. It'll just work. For example:
NSMutableDictionary *outer=[[NSMutableDictionary alloc] init];
NSDictionary *inner=[NSDictionary dictionaryWithObject:#"hello" forKey:#"world"];
[outer setObject:inner forKey:#"greetings"];
I want to parse data from a xml file. This is working so far, but I don't find the correct way to get data from the dictionary.
My parser looks as follows
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict{
currentElement = [elementName copy];
if ([elementName isEqualToString:#"Placemark"]) {
placemarkData = [[NSMutableDictionary alloc] init];
currentTitle = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
if ([currentElement isEqualToString:#"name"] || [currentElement isEqualToString:#"address"] || [currentElement isEqualToString:#"coordinates"])
[currentTitle appendString:string];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName{
if ([elementName isEqualToString:#"Placemark"]) {
[placemarkData setObject:currentTitle forKey:currentElement];
[Placemarks addObject:[placemarkData copy]];
NSLog(#"adding story: %#", currentElement);
}
As you can see. The data is stored in a dictionary, and this dictionary is store in an array. Only problem is... the key for dictionary is always coordinates. This look kinda logical to me... but I don't find the logic to implement it the right way. Can somebody have a look?
Thnx in advance!
When you fill a dictionary, you set objects for keys. To get an object from a dictionay, you need to have the key. And I think that is your problem.
To list the keys in your dictionary, you can use a keyEnumerator:
NSEnumerator *enumerator = [myDictionary keyEnumerator];
id key;
while ((key = [enumerator nextObject])) {
/* code that uses the returned key */
NSString *theElement = key;
NSLog(#"Element: %#", theElement);
}
With this code, you will list all the keys. To find the object with the key, you use [myDictionary objectForKey:key];. If you know the elements in your XML you can access them with this line.
I think the problem you are facing is because of the logic you used in
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict{
currentElement = [elementName copy];
if ([elementName isEqualToString:#"Placemark"]) {
placemarkData = [[NSMutableDictionary alloc] init];
currentTitle = [[NSMutableString alloc] init];
}
}
method, i.e. allocating the placemarkData that will work fine if element Placemark is encountered first time but for the next iteration there will be problem, follow this idea and see the response on to the console, I am sure that you will find the solution.
come back if any doubt or query in that.