Parsing XML elements for a TableView - objective-c

At the moment I am designing a simple app that will read news feeds. The XML feed I am using is this... http://feeds.foxnews.com/foxnews/world?format=xml .
What I am looking to do is essentially take the 'title' element and the description' elements from this page, and use them in a TableView.
This is what I currently have:
-(void)parserDidStartDocument:(NSXMLParser *)parser{
_elementsArray = [[NSMutableArray alloc]init];
}
-(void)parser:(NSXMLParser*)parser foundCharacters:(NSString *)string{
if (!_currentString) {
_currentString = [[NSMutableString alloc]init];
}
[_currentString appendString:string];
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if ([elementName isEqualToString:#"title"]) {
[_elementsArray addObject:_currentString];
_currentString=nil;
return;
}
if ([elementName isEqualToString:#"description"]) {
[_elementsArray addObject:_currentString];
_currentString=nil;
return;
}
_currentString = nil;
}
-(void)parserDidEndDocument:(NSXMLParser *)parser{
for (NSString*string in _elementsArray) {
NSLog(#"%#",string);
}
_elementsArray = nil;
}
I do get an output of this, that is not a problem.
My problem is that each element is added in a new NSArray entry. How would I add both elements to one entry? ( would it be possible to use keys ? )

You could use a temporary NSDictionary to store your title and description together for each item, and add the dictionary to the _elementsArray.
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict {
if ([elementName isEqualToString:#"item"]) {
_currentDict = [NSMutableDictionary dictionary];
return;
}
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if ([elementName isEqualToString:#"item"]) {
[_elementsArray addObject:_currentDict];
_currentDict=nil;
return;
}
if ([elementName isEqualToString:#"title"]) {
_currentDict[#"title"] = _currentString;
_currentString=nil;
return;
}
if ([elementName isEqualToString:#"description"]) {
_currentDict[#"description"] = _currentString;
_currentString=nil;
return;
}
_currentString = nil;
}
// Other methods left unchanged
This should output something like:
NSArray (
NSDictionary {
#"title": #"First title",
#"description": #"A news article"
},
NSDictionary {
#"title": #"Second title",
#"description": #"Another interesting article"
}
)
Untested, but I hope you get the idea.
Note also that parsing an XML (or RSS) file into native ObjC objects is a very recurring problem, and lots of open source libs out there already do most of the heavy lifting for you.

Related

Parsing with NSXML parser to same roots elements

<News TotalRecords="10">
<News ID="381222" DateTime="2013-06-12T00:05:00" Title="abc" HeadlineImgSrc="http://www.msa.net/common/news/images/381222/headline.jpg"/>
<News ID="381192" DateTime="2013-06-11T16:46:00" Title="asdsad" HeadlineImgSrc="http://www.asdsad.net/common/news/images/381192/headline.jpg"/>
<News ID="381085" DateTime="2013-06-11T11:34:00" Title="sdsadsadsadsad" HeadlineImgSrc="http://www.asdsad.net/common/news/images/381085/headline.jpg"/>
<News ID="381042" DateTime="2013-06-11T09:16:00" Title="asdsad" HeadlineImgSrc="http://www.asdsad.net/common/news/images/381042/headline.jpg"/>
<News ID="380972" DateTime="2013-06-11T00:01:00" Title="asdsad" HeadlineImgSrc="http://www.asdsad.net/common/news/images/380972/headline.jpg"/>
<News ID="380908" DateTime="2013-06-10T16:23:00" Title="asdsad" HeadlineImgSrc="http://www.asdsad.net/common/news/images/380908/headline.jpg"/>
</News>
I want to parse this xml. But it is root name start with News, other element start with also News.
This is what I have tried (without success)
-(void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
if ([elementName isEqualToString:#"News"]) {
app.listArray = [[NSMutableArray alloc] init];
} else if([elementName isEqualToString:#"News"] ){
theList = [[List alloc] init];
theList.ThumbImageURL = [attributeDict objectForKey:#"HeadlineImgSrc"];
theList.Title = [attributeDict objectForKey:#"Title"];
theList.URL = [attributeDict objectForKey:#"DateTime"];
}
}
How can I parse this type of XML using the NSXMLParser?
You could check for the existence of TotalRecords:
-(void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
if ([elementName isEqualToString:#"News"])
{
NSString *totalRecords = attributeDict[#"TotalRecords"];
if (totalRecords)
{
app.listArray = [[NSMutableArray alloc] init];
}
else
{
theList = [[List alloc] init];
theList.ThumbImageURL = [attributeDict objectForKey:#"HeadlineImgSrc"];
theList.Title = [attributeDict objectForKey:#"Title"];
theList.URL = [attributeDict objectForKey:#"DateTime"];
[app.listArray addObject:theList];
}
}
}
Frankly, I'd rather see you fix the XML source, as it's a poor design to use the same element name for different purposes, but if you're stuck with the existing XML structure, something like the above would handle it.
You can use the attributes for distinguish them.
Try this code:
-(void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([attributeDict objectForKey:#"TotalRecords"])
{
app.listArray = [[NSMutableArray alloc] init];
}
else if([attributeDict objectForKey:#"ID"])
{
theList = [[List alloc] init];
theList.ThumbImageURL = [attributeDict objectForKey:#"HeadlineImgSrc"];
theList.Title = [attributeDict objectForKey:#"Title"];
theList.URL = [attributeDict objectForKey:#"DateTime"];
}
}

NSXMLParser to create nodes and NSMutableArray

i'm looking for a advise. I want to parse an XML file with NSXMLParser, and i wonder what i should do with tags and parameters.
Fo example i have:
<template>
<template name="default" layout="absolute">
<image tmpl="topbanner"/>
<list tmpl="list">
<font tmpl="listfont"/>
<item target="target1">
<text>Target1</text>
</item>
<item target="target2">
<text>Target2</text>
</item>
<item target="target3">
<text>Target3</text>
</item>
.
.
.
And later i want to create an objects base on this information. So - where should i store retrieve information from parser? In method:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
element = [NSMutableString string];
}
I see that i can simply recieve atributes ang tags, but should I in this point write it in NSMutableArray or NSDictionary?
I've read NSXMLParser how to pass the NSMutableDictionary to a NSMutableArray But is it the best way?
If you know that a <list> element contains an array of subelements, you'll probably want to create an array or dictionary at the start of that block:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqualToString:#"list"]) {
self.list = [NSMutableDictionary dictionary];
self.listName = [attributeDict objectForKey:#"tmpl"]
}
else if ([elementName isEqualToString:#"item"]) {
self.itemKey = [attributeDict objectForKey:#"target"];
}
else if ([elementName isEqualToString:#"text"]) {
self.data = [NSMutableString string];
}
else if ([elementName isEqualToString:#"font"]) {
self.font = [attributeDict objectForKey:#"tmpl"];
}
}
Then you can add the simple <item> elements in your -parser:didEndElement:... method:
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if (elementName isEqualToString:#"text") {
// no action needed here -- data already contains the text
}
else if (elementName isEqualToString:#"item"]) {
[self.list setObject:[self.data copy] forKey:self.itemKey;
self.itemKey = nil;
}
else if ([elementName isEqualToString:#"list"]) {
// do something appropriate with the list
[self.template setObject:self.items forKey:self.listName];
self.listName = nil;
self.list = nil;
}
}
This all presumes that you've got properties for font, data, itemKey, and so on... basically whatever you need to remember all the state you need until you can create the related object. Once you have the data you need, usually in the didEnd method, create the object, store it somewhere, and clear out the saved data.
This isn't the only approach to parsing the data. For example, you might want to go with a stack-based approach instead. But the idea illustrated above is probably the easiest to understand, and if the data isn't too complicated it's not hard to manage.
To get what's inside the tag use:
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
[element appendString:string];
}
Then check in :
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
To get the attributes :
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
if ([elementName isEqualToString:#"image"]) {
NSString *imageName = [attributeDict objectForKey:#"tmpl"];
}
}

XML parsing in IOS Shows NULL value in label?

i m doing XML parsing using NSXMLParserDelegate..I m getting null value when i print the value in label...I m using simple xml file got in an internet.You can check what is in that file too.anyone pls help..Here is my code
#import "xmlViewController.h"
#implementation xmlViewController
#synthesize label;
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL *url=[NSURL URLWithString:[NSString stringWithFormat:#"http://www.bonifacedesigns.com/tuts/xmltest.xml"]];
receivedData=[[NSMutableData alloc]initWithContentsOfURL:url];
dataParser=[[NSXMLParser alloc]initWithData:receivedData];
dataParser.delegate=self;
[dataParser parse];
}
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
// if(!xmlString){
//
// }else
// {
// [xmlString setString:#""];
// }
if([elementName isEqualToString:#"elements"])
{
xmlString=[NSMutableString alloc];
[xmlString setString:#""];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
xmlString= (NSMutableString *)[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if([elementName isEqualToString:#"element"]){
NSString *string=xmlString;
[xmlArray addObject:string];
label.text=[NSString stringWithFormat:#"%#",xmlArray];
}
}
#end
This is my XML file....I want to print XML Data Parsed! in my label.Help me with that.I have edited the code and check now
<elements>
<element myData="XML Data Parsed!"/>
</elements>
check this:
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
// if(!xmlString){
//
// }else
// {
// [xmlString setString:#""];
// }
if([elementName isEqualToString:#"elements"])
{
xmlString=[[NSMutableString alloc]initWithCapacity:5];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
xmlString= (NSMutableString *)[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if([elementName isEqualToString:#"element"]){
[attributeDict objectForKey:#"myData"];
[xmlString setString:#""];
NSString *string=xmlString;
[xmlArray addObject:string];
label.text=[NSString stringWithFormat:#"%#",xmlArray];
}
I m getting error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with setString:'
*** First throw call stack:
(0x13ba052 0x154bd0a 0x1362a78 0x13629e9 0x1387970 0x2a26 0x9e1a35 0x5171532 0x9e002d 0x26b8 0xd764e 0x37a73 0x37ce2 0x37ea8 0x3ed9a 0xfbe6 0x108a6 0x1f743 0x201f8 0x13aa9 0x12a4fa9 0x138e1c5 0x12f3022 0x12f190a 0x12f0db4 0x12f0ccb 0x102a7 0x11a9b 0x2238 0x2195 0x1)
terminate called throwing an exception
It seems you have several errors in your code.
The mutable string xmlString isn't properly initialized, on the line :
xmlString = [NSMutableString alloc]; // wrong
This should look like : xmlString = [[NSMutableString alloc] initWithCapacity:X];.
Also, when the parser finds characters, you replace the entire existing mutable string, instead of appending them to the existing content, or try to because according to the code, you simply make a memory address compare and waste the result (==).
// wrong
xmlString == (NSMutableString *)[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
This should look like :
[xmlString appendString:[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
Finally in didEndElement, I'm not sure what you are trying to achieve, but you add the string object reference to an array, and then set the label text to display the content of the array... as long as the mutable array is well allocated and initialized (what I'm really not sure), the label should display at least the array content. But you might consider to "flush" the mutable string ([xmlString setString:#""];) and not allocate it on every element.
EDIT :
The attributeDict maps attribute names as the keys with their values. I guess your attribute value can be accessed via [attributeDict objectForKey:#"myData"];.
I guess that the problem is in this line:
xmlString== (NSMutableString *)[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
Change == to =
Hope that explanation isn't needed.

Parsing XML using NSXML for iPhone

I have an XML which has a structure like this:
<data>
<books>
<item>
<title>book1</title>
<author>author1</author>
</item>
<item>
<title>book2</title>
<author>author2</author>
</item>
</books>
<magazines>
<item>
<title>Mag1</title>
<author>author1</author>
</item>
</magazines>
<journals>
<item>
<title>Journal1</title>
<author>author1</author>
</item>
<item>
<title>Journal2</title>
<author>author2</author>
</item>
</journals>
</data>
I am using a table view to display the items of each category. I plan to have a table view with columns books, magazines & journals and clicking on them should display only the items under them. That is, if I click on books, it should only display the book entries.
I have this for xml parsing
- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *) elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqual:#"books"]) {
flag=1;
}
if ([elementName isEqual:#"title"]) {
NSLog(#"found title!");
parsedString = [[NSMutableString alloc] init];
}
if ([elementName isEqual:#"author"]) {
NSLog(#"description found");
parsedString = [[NSMutableString alloc] init];
}
}
and in
- (void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
{
if (flag==1){
if ([elementName isEqual:#"title"]) {
[headlines addObject:parseString];
[parseString release];
parseString = nil;
}
if ([elementName isEqual:#"author"]) {
[authors addObject:parsedString];
[parseString release];
parseString = nil;
flag=2;
}
}
}
But this gets only the first entry of books and nothing else.
Instead of setting flags is there a way to parse only between the <books> and
</books> tags ?
Thank you.
The reason it is only getting the first item is that you are setting flag to 2 when you see the end element for the author tag. I suspect you will get closer to what you want if you update this flag in the end element for the books tag, as follows:
- (void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
{
if (flag==1){
if ([elementName isEqual:#"title"]) {
[headlines addObject:parseString];
[parseString release];
parseString = nil;
}
if ([elementName isEqual:#"author"]) {
[authors addObject:parsedString];
[parseString release];
parseString = nil;
}
if ([elementName isEqual:#"books"]) {
flag=2;
}
}
}
Just cancel parsing when the books tag ends:
- (void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
{
if ([elementName isEqual:#"books"]) {
[parser abortParsing];
}
....
}

Can't copy mutable array value into string

i really appreciate for immediate reply and i submit my xml parser methods to you and at last problem is mention.
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
currentElement = [elementName copy];
if([elementName isEqualToString:#"alluser"])
{
objUser = [[Users alloc] init];
}
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if([elementName isEqualToString:#"username"])
{
objUser.userName =[currentElementValue stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSLog(#"%#",objUser.userName);
}
if([elementName isEqualToString:#"password"])
{
objUser.passWord = [currentElementValue stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSLog(#"%#",objUser.passWord);
}
if([elementName isEqualToString:#"user"])
{
[usersArray addObject:objUser];
[objUser release];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if(!currentElementValue)
{
currentElementValue = [[NSMutableString alloc] initWithString:string];
[currentElementValue stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
else
{
[currentElementValue appendString:string];
[currentElementValue stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
}
if([currentElementValue isEqualToString:#"User"])
{
[currentuser appendString:string];
}
if([currentElementValue isEqualToString:#"Pass"])
{
[currentpass appendString:string];
}
}
-(IBAction)backgroundclick:(id)sender
{
[txtpass resignFirstResponder];
[txtusername resignFirstResponder];
}
-(IBAction)returnPressed:(id)sender
{
[sender resignFirstResponder];
}
-(IBAction)LoginClicked:(id)sender
{
int cnt=0;
NSString *currUser = txtusername.text;
// NSString *passWord = txtpass.text;
// int i=0;
for(Users *objUser in usersArray )
{
/// how to get value and how to compare string...
}
return;
}
my mutable array is in this code is (usersArray). i got parse data into the array and i want to check username and password with textfields value if it match with array the certain action is performed. but somehow i cant access data into the string.
You don't give much detail as to what the problem is, so just guessing... For your loop in LoginClicked() you can compare using:
[currUser isEqualToString:objUser.userName]
Or any other string comparison method. But maybe your problem is something else? In your "parser:didStartElement..." method did you mean to check for the "user" element rather than "allUser"? As it is you allocate only one Users object but attempt to parse many and insert them into an array.