Leak in the NSXML Parser - iphone

It seems like there is memory leak in this piece of code.I am using this to parse XML data.
- (void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict
{
currentElement = [[elementName copy]autorelease];
if ([elementName isEqualToString:#"value1"]) {
self.currentString =[NSMutableString string];
}
else if ([elementName isEqualToString:#"value2"]) {
self.currentStringName =[NSMutableString string];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if ([currentElement isEqualToString:#"value1"]) {
[currentString appendString:string];
}
else if ([currentElement isEqualToString:#"value2"]) {
[currentStringName appendString:string];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:#"value1"]) {
}
else if ([elementName isEqualToString:#"value2"])
{
}

You may want to make a little research of the style "NSXMLParser leak". Like a few other pieces of the SDK, NSXMLParser is a broken dam. I dont see in your code (after, I must say, a very quick glance) any leaks... I mean compared to what you'll find in NSXMLParser. And unfortunately, you can't do anything about them.
So, basically, if Instruments, for example, is reporting leaks with your code, don't be ashame: NSXMLParser is responsible.
If you have the chance, don't hesitate to keep control of the objects you create (and avoid autorelease), it's way easier to manage in my opinion (but...some could disagree!).

Try using other XML Parsers like touchXML or KissXML. NSXML Parser does have leaks inside the framework.

Related

Difficulty parsing RSS feed

I'm trying to parse an RSS feed (http://www.themostunrealbeats.com/?feed=rss2) using NSXMLParser. I am having difficulty finding the picture in the article. Here is where the picture is in the RSS feed.
<media:content url="http://themostunrealbeats.files.wordpress.com/2012/03/madeon.png?w=400" medium="image">
<media:title type="html">madeon</media:title>
</media:content>
Specifically, I want http://themostunrealbeats.files.wordpress.com/2012/03/madeon.png. Yet in the delegate method for NSXMLParser, I don't find anything.
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if ([element isEqualToString:#"media:content"]) {
NSLog(#"%#", string);
[content appendString:string];
}
}
string has no value. How can I parse this?
// NSXMLParser has a following method
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
// In this method parameter 'attributeDict' will return you all the sub attributes of main attribute.
// In your case its 'url' of Picture.
// I hope this will help you. Check this out.

Parse an XML file with same tags

I am having an XML file which needs to be parsed.
I have parse an XML before but it was simpler. Because this file has similar tags, I am not able to figure out how to parse it.
The xml file looks like this:
<resource classname="Quote">
<field name="name">USD/KRW</field>
<field name="price">1104.319946</field>
<field name="symbol">KRW=X</field>
<field name="ts">1350544190</field>
<field name="type">currency</field>
<field name="utctime">2012-10-18T07:09:50+0000</field>
<field name="volume">-56144</field>
</resource>
I want to get the price field and add it to an array. I tried the code below from some online examples but it's showing all field's values and I want only the price field.
- (void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([[attributeDict objectForKey:#"name"] isEqualToString:#"price"])
{
currencyValuesString = [[NSMutableString alloc]init];
}
}
- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementname isEqualToString:#"field"])
{
[currencyValuesArray addObject:currencyValuesString];
}
}
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
currencyValuesString = (NSMutableString *) [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
There is multiple problems with your code:
You alloc/init currencyValuesString when you enter your <field name="price"> tag, but you never release it (which will leak if you are not using ARC), and more importantly you don't use this allocated instance, because in your -parser:foundCharacters: you replace the value of this variable with a different string.
Thus the previously allocated NSMutableString is totally useless. And moreover, in case your fields would contain multiple chucks of text instead of one (<field name="price">1.234<b>€</b></field> for example), -parser:foundCharacters: would be called multiple times, but your code will always throw away the previous value and keep only the last chunk instead of adding them to the NSMutableString.
If fact, with your code there is no point having an NSMutableString as you don't use it as a mutable string but simply replace each value with a new one each time instead of appending it.
But also, each time you encounter a closing </field> tag, whatever this tag is, you perform [currencyValuesArray addObject:currencyValuesString]; but don't release the currencyValuesString (which is fine only if you use ARC, but will leak if you use Manual Reference Counting) and more importantly you will add this string to the array each time you encounter the closing </field> tag, whatever the name of the opening tag was. So you will add the price to your array as many times as you have </field> closing tags after your price.
So the right approach would be to:
Append the foundCharacters to your NSMutableString instead of replacing it
When you encounter the closing tag, add the price to the array but then release and set the price string back to nil immediately. This way the currencyValuesString will only be around between the <field name="price"> opening tag and its corresponding </field> closing tag, and not when parsing the other <field> tags
Only add the price to your array in the closing tag if the currencyValuesString is not nil
- (void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([[attributeDict objectForKey:#"name"] isEqualToString:#"price"])
{
currencyValuesString = [[NSMutableString alloc]init];
}
}
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
[currencyValuesString appendString:[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
}
- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementname isEqualToString:#"field"] && (currencyValuesString != nil))
{
[currencyValuesArray addObject:currencyValuesString];
[currencyValuesString release]; // Only needed if not using ARC. Remove this line if you use ARC
currencyValuesString = nil; // so that the next closing tag won't readd the price
}
}
try this :
in .h
NSXMLParser *rssParser;
NSDictionary *myAttrDict;
NSString *currentElement;
NSMutableArray *prizeArr;
in .m:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if([elementName isEqualToString:#"field"])
{
if([[attributeDict objectForKey:#"name"] isEqualToString:#"price"])
{
currentElement = #"price";
}
else{
currentElement = nil;
}
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if([currentElement isEqualToString:#"price"])
{
[prizeArr addObject:string];
}
NSLog(#"prizeArr :%#",prizeArr);
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
}
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
}

Parsing XML file with same name attributes in iphone using NSXMLParser?

I am creating an iPhone App in which I have to consume values from a service url.The response from url is of XML format.XML file has same name attributes many times,and those attributes are not static.I mean the no of attributes may increase from time to time. So I couldn't understand how to consume that XML response in iPhone.
My XML response looks like this :
GetUserResponse xmlns="http://schemas.datacontract.org/2004/07"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<CompanyList>
<User>
<Address>
<City>Alabaster</City>
<State>Alabama</State>
</Address>
<Employee>
<Name>DrJohn</Name>
</Employee>
</User>
<User>
<Address>
<City>SanFransisco</City>>
<State>California</State>
</Address>
<Employee>
<Name>DrWilliams</Name>
</Employee>
</User>
</CompanyList>
</GetUserResponse>
The thing is I couldn't say there will be specific number of tags as of 2 tags here.They may or may not increase from time to time.I think we should take something like count number of items and extract the values but couldn't understand how?
This is one of many ways to do it. You can simply get the attributeDict and parse all the values from it using key-value.
NSString *element;
- (void) viewDidLoad {
[super viewDidLoad];
cityArr = [[NSMutableArray alloc] init];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
element = elementName;
if ([elementName isEqualToString:#“Address”]) {
// initialize stings to store values of child elements
cityStr = [NSMutableString alloc] init];
}
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
if ([element isEqualToString:#“City”])
cityStr appendString:string];// Append Values
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#“Address”]) {
[cityArr addObject:cityStr]; // Add strings to array/dictionary and release string
}
}

NSXMLParser issue : don't get all data?

I'm developing an application that needs to get data from an XML file. Some of the nodes have a lot of characters and I've a problem using this function :
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
currentNodeContent = (NSMutableString *) [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
For example for the description node of an item I will get only the 20-30 last characters whereas I would get 200 or 300.
I checked this out with a NSLog and it appears the problem comes from here. Do you know what's wrong ?
Thanks for any advice.
SAX parsers do not guarantee to get all characters at once. You may get multiple calls with chunks of characters from any given block; your code should concatenate them into a single string.
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.
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict {
if ([qualifiedName isEqualToString:#"myTag"]) {
buf = [NSMutableString string];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([qualifiedName isEqualToString:#"myTag"]) {
buf = [buf stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSLog(#"Got %#", buf);
}
}
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
[buf appendString:string];
}

xml parsing iphone issue

I'm using the NSXMLParser however i dont quite understand how to display items from a xml correctly. E.g. i have this in the xml parser:
- (void) parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqualToString:#"level4"]) {
//add a book object to the array at the index determined by bookCount
self.bookCount++;
[books addObject:[[book alloc]init]];
//[books insertObject:[[book alloc]init] atIndex:self.bookCount];
}
if ([elementName isEqualToString:#"module"]) {
isFirstName = YES;
}
if ([elementName isEqualToString:#"moduleTitle"]) {
isLastName = YES;
}
if ([elementName isEqualToString:#"semester"]) {
isTitle = YES;
}
if ([elementName isEqualToString:#"assessmentName"]) {
isGenre = YES;
}
}
This is my xml
<myCourse>
<courseName>BEng Mobile and Web Computing</courseName>
<courseStructure>
<level4>
<module>
<moduleCode>ECSC401</moduleCode>
<moduleTitle>Programming Methodology</moduleTitle>
<credits>15</credits>
<semester>1</semester>
<assessmentDetails>
<assessment>
<assessmentName>Test1</assessmentName>
<assessmentType>Coursework</assessmentType>
<assessmentWeighting>30</assessmentWeighting>
<assessmentDueDate/>
</assessment>
<assessment>
<assessmentName>Coursework</assessmentName>
<assessmentType>Coursework</assessmentType>
<assessmentWeighting>40</assessmentWeighting>
<assessmentDueDate/>
</assessment>
<assessment>
<assessmentName>Test2</assessmentName>
<assessmentType>Coursework</assessmentType>
<assessmentWeighting>30</assessmentWeighting>
<assessmentDueDate/>
</assessment>
</assessmentDetails>
</module>
</level4>
</courseStructure>
</myCourse>
(it continues level 1,level2,level3 using the same format). I simply want to display all the modules under the 'level4' hierarchy - how can i do this?/what am i doing wrong? the items are displaying just not the right ones ..
First of all you have to create one NSMutableDictionary. When you get the tag in didStartElement Method initiate this Dictionary.And also set one flag.
And in Found character method check that flag, if It is then store that tagValues and in didEndElement store all the values in this Dictionary when you find .
You can find all levels values using this.
Hope this will help you.
Use both methods:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
and
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
For parsing.