feed reader using nsxmlparserdelegate - objective-c

i am creating an app that shows the latest feeds from a particular webservice,i am using NSXMLParserDelegate protocol for this purpose,well i read the apple documentation and i tried some tutorials too,but something seems to be going wrong somewhere,i dont understand how does the didEndElement,foundCharacters work,anyways i want to display the image,title and content,pub-date of the post,i am newbie to xmlparsing here's my viewcontroller.h(i have just parsed only the title element in the following code)
#property(nonatomic,strong)NSString *currentElement;
#property(nonatomic,strong)NSString *currentTitle;
#property(nonatomic,strong)NSMutableArray *titles;
viewdidload
NSURL *url=[NSURL URLWithString:#"http://www.forbes.com/fast/feed"];
NSXMLParser *parser=[[NSXMLParser alloc]initWithContentsOfURL:url];
[parser setDelegate:self];
[parser parse];
NSLog(#"%d",titles.count);
didStartElement
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
self.currentElement=elementName;
if ([self.currentElement isEqualToString:#"title"])
{
self.currentTitle=[NSMutableString alloc];
titles=[[NSMutableArray alloc]init];
titles=[attributeDict objectForKey:#"title"];
}
}
foundCharacters
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
self.currentElement=elementName;
if ([self.currentElement isEqualToString:#"title"])
{
self.currentTitle=[NSMutableString alloc];
titles=[[NSMutableArray alloc]init];
titles=[attributeDict objectForKey:#"title"];
}
}
didEndElement
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if([self.currentElement isEqualToString:#"title"])
{
NSLog(#"%#",self.currentTitle);
}
}
doubts
1)where am i supposed to declare my titles array so that i can add individual title object to it.
what is the use of [attributeDict objectForKey] in didStartElement? it returned null for my program
2)what does foundCharacters delegate actually do? what does it append?
3)After didEndElement why doesnt the compiler reach for didStartElement and not the foundCharacters ?
4)finally should i actually use NSXMLParserDelegate protocol for the xml parsing,do others like touchXML,TBXML and others provided in the raywenderlich make a difference?
i am sorry for the long post,but i havent got any satisfying answers online regarding my queries,i used all the breakpoints and figured out how the delegates are called back and forth,i need some enlightening answers to my queries,thanks and sorry

1. Declare your array before starting to parse.Whenever you meet an elements (an xml tag), initialize the elements (set some BOOL in the class in a way that you can recognize what element you are reading);
2. Found characters are the characters found as value of a tag.If you know what element you are reading (reading your instance variables), you should append this string to your temporary NSMutableString and add it to the array only when the element is ended.
3. Because it doesn't start finding other characters until a new tag is reached.
Example
I see that you are confused, let's say that you have this XML code:
<person> mickey mouse </person>
When you meet the tag the element starts, then you find other characters (not the entire string, just a part of the string) until the string ends, then when you meet the tag the element is ended.

in didFindCharacters just add the found characters to a NSMutableString and in didEndElement you know what you ended and set a variable to the strings you found
image:
image => the character at didEnd are the name of the link, the url is in the attributes past in didStart
title-tag
didStart => didStart : html started, every tag is html until the didEnd of title-tag
content:
didStart => didStart : html started, every tag is html until the didEnd of content

Related

Parsing With NSXML Parser Cocoa

I am trying to parse the following information from xml in Cocoa.
<level>
<entity>
<name>red</name>
<id>0</id>
<body>false</body>
<x>0.0</x>
<y>0.0</y>
<rotation>0.0</rotation>
</entity>
Here is what i have so far from following the nsxml parser guide by apple.
NSString* currentElement;
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
currentElement = elementName;
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
if([currentElement isEqualToString:#"name"]){
NSLog(#"Name found: %#", string);
}
}
In my found character method it logs the statement but the only thing returning is "Name found: " and the rest is blank.
Am i doing this correctly to get the following tags in my xml? I would like to extract each tag.
for example:
NSString* name = THE_NAME;
int x = [THE_X_VALUE, intValue];
etc.
Could anyone help me out?
NSXMLParser is one of those classes that sounds good on paper, but drives you insane when you actually start to use it. If you really want to continue on this joyless path, implement parser:didStartElement: and then pray that parser:foundCharacters: delivers the goods.
In my experience, NSXMLParser is like a big toddler who smashes the puzzle and then hands you the pieces one by one. That seems like a great at first: "Oh look, I've got a piece! And another one! And one more!" but soon you find yourself bewildered: "Oh look, I've got 5.0! Now, was that the rotation or the y coordinates?" Who knows? Not NSXMLParser, that's for sure.
As for the last line of code: to extract the numeric value of a string, NSDecimalNumber is very handy.
if([currentElement isEqualToString:#"x"]){
NSDecimalNumber *dec = [NSDecimalNumber numberWithString:string];
CGFloat x = dec.floatValue;
}

Parsing XML CDATA Blocks

I'm attempting to parse an XML file (using NSXMLParser) from the website librarything.com. This is the first file I have ever parsed, but for the most part it seems fairly straight forward. My problem occurs when trying to parse a CDATA block; the method parser:foundCDATA: isn't called, and I can't understand why. I know my parser is set up properly because the parser:foundCharacters: method works fine. The XML data I am trying to parse looks like this http://www.librarything.com/services/rest/1.1/?method=librarything.ck.getwork&isbn=030788743X&apikey=d231aa37c9b4f5d304a60a3d0ad1dad4 and the CDATA block occurs inside the element with the attribute name "description".
Any help as to why the method is not being called would be greatly appreciated!
EDIT: I ran the parser:foundCharacters: method on the description CDATA block and it returned "<". I'm assuming this means that the parser is not seeing the CDATA tag correctly. Is there anything that can be done on my end to fix this?
It appears the CDATA contents in the <fact> tags is being returned incrementally over multiple call backs in parser:foundCharacters. In you class where you are conforming to NSXMLParserDelegate try building up the CDATA by appending it to an NSMutableString instance, like so:
(Note: here _currentElement is an NSString property and _factString is an NSMutableString property)
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict {
self.currentElement = elementName;
if ([_currentElement isEqualToString:#"fact"]) {
// Make a new mutable string to store the fact string
self.factString = [NSMutableString string];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#"fact"]) {
// If fact string starts with CDATA tags then just get the CDATA without the tags
NSString *prefix = #"<![CDATA[";
if ([_factString hasPrefix:prefix]) {
NSString *cdataString = [_factString substringWithRange:NSMakeRange((prefix.length+1), _factString.length - 3 -(prefix.length+1))];
// Do stuff with CDATA here...
NSLog(#"%#", cdataString);
// No longer need the fact string so make a new one ready for next XML CDATA
self.factString = [NSMutableString string];
}
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if ([_currentElement isEqualToString:#"fact"]) {
// If we are at a fact element, append the string
// CDATA is returned to this method in more than one go, so build the string up over time
[_factString appendString:string];
}
}

How to handle a tags inside another tags in NSXMLParser

I have a file:
<xml>
<component>something
<system>somethingDeeper
<value>somethingDeepest</value>
</system>
</component>
<component>somethinfDifferent
<value>somethingDifferentDeeper</value>
</component>
<value>somethingNew</value>
</xml>
So I want to distinguish what is inside another tag (ex. <system>) what is not. How to do this with NSXMLParser? I currently use BOOL ivar's but this is a lot of tags and this is not as elegant as I want it to be. I know that NSXMLParser is a SAX parser and I understand that.
In above example I will be enter to didEndElement method three times with:
elementName equal value Is there a more elegant way to distinguish what entry was from <component> tag above what not?
You could keep an array of tag names you are currently within
NSMutableArray *tagNameStack;
Each time you enter a new element you add it to this array. Each time you leave you remove it again. i.e.
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict {
[tagNameStack addObject:elementName];
// Your code here
}
and
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
// Your code here
[tagNameStack removeLastObject];
}
So, when you are parsing somethingDeepest your tagNameStack array would be
#"xml", #"component", #"system", #"value"
You can use this stack to decide where you are in the xml i.e.
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if ([tagNameStack containsObject:#"system"]) {
// Deal with the value inside the system tag
} else if ([tagNameStack containsObject:#"component"]) {
// Deal with the value inside the component tag
} else {
// This must be the value inside the XML tag
}
}

Cut out a part of a long NSString

In my app I want to show a String that contains news. This string is being loaded just from a free Website, so the plain source code of the website does not contain only my string, its is more os less like this:
Stuff
More Stuff
More HTML Stuff
My String
More HTML Stuff
Final Stuff
And of course i want to cut off all the html stuff that i don't want in my NSString. Since i am going to change the String fron time to time the overall length of the Source code from the website changes. This means that substringFromIndex wont work. Is there any other way to Convert the complete source code to just the String that i need?
There are zillions of ways to manipulate text. I would start with regular expressions. If you give more details about the specifics of your problem, you can get more specific help.
Edit
Thanks for the link to the website. That gives me more to work with. If you will always know the id of the div whose contents you want, you can use NSXMLParser to extract the text of the div. This will set the text of an NSTextField to the contents of the div with id "I3_sys_txt". I did this on the Mac but I believe it will work on the iPhone as well.
-(IBAction)buttonPressed:(id)sender {
captureCharacters = NO;
NSURL *theURL = [NSURL URLWithString:#"http://maxnerios.yolasite.com/"];
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:theURL];
[parser setDelegate:self];
[parser parse];
[parser release];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict {
if ([elementName isEqual:#"div"] && [[attributeDict objectForKey:#"id"] isEqual:#"I3_sys_txt"]) {
captureCharacters = YES;
divCharacters = [[NSMutableString alloc] initWithCapacity:500];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if (captureCharacters) {
//from parser:foundCharacters: docs:
//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.
[divCharacters appendString:string];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if (captureCharacters) {
captureCharacters = NO;
[textField setStringValue:divCharacters];
[divCharacters release];
}
}
Here is the NSRegularExpression class that you need to use: NSRegularExpression
Here is a 'beginning' tutorial on how to use the class: Tutorial
Here is a primer on what regular expressions are: Regex Primer
Here is an online regular expression tester: Tester
The tester may not work exactly as NSRegularExpression but it will help you understand regex definitions in general. Regular expressions are a key tool for software developers, a little daunting at first, but they can be used to great effect when searching or manipulating strings.
Although this looks like a lot of work - there is no 'quick answer' to what you are attempting. You say "is there any other way to Convert the complete source code to just the String I need?' - the answer is yes - regular expressions. But you need to define what 'just the String that I need' means, and regular expressions are one important way.

get XML content at node in Objective C

Using Objective C - I want to loop through an XML tree and display the full XML content at each instance of a specific node matching with a particular element name.
As an example - I am looking to get the XML (represented as an NSString) within each instance of element b. I can get the value if there is only a string in element b, but how do I get an NSString representation formatted as XML including all the element names?
<element a>
<element b>
<element c>
some text 1
</element c>
</element b>
<element b>
<element c>
some text 2
</element c>
</element b>
<element b>
<element c>
some text 3
</element c>
</element b>
</element a>
You can use NSXMLParser to do this. The three main sections of the class (1) check for opening tags:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict;
(2) grab the content within the node encountered:
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string;
and (3) store the data to an appropriate place of your choice on node completion:
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName;
Apple has an excellent guide covering NSXMLParser here
There are possibly three approaches I can think of, all of which have the pros and cons.
Parse as a string. Depending on what exactly you want to extract, you could just use string manipulation code to search and extract the bits you want. This ignores the xml structure and reduces the problem to a simple string processing one. But may not be clever enough depending on the xml and the nodes you want.
Code up a NSXMLParser to parse the xml string into some sort of datastructure (i.e. DOM), locate the nodes you want in it and then get them to generate out the xml. Again depending on what you want, this could be too heavy. COuld also mean a lot more coding.
Download a library to do the dirty work for you. There are a variety around and I have one at (Shameless self promotion here) http://github.com/drekka/dXml which may help. It can certainly parse the xml into a DOM like data structure from which you can use XPath like queries to find the nodes and convert back to strings. The cons of this is that you are now using a third party library.
I had a brainwave last night and here is the solution I came up with... seems a little inelegant, but it works.
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
if ([elementName isEqualToString:#"element b"])
{
area = [[NSMutableString alloc] init];
current_element = [elementName copy];
}
else if ([current_element isEqualToString:#"element b"])
{
NSString *tag = [NSString stringWithFormat:#"<%#>", elementName];
[area appendString:tag];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if ([elementName isEqualToString:#"element b"])
{
NSLog(#"tag info: %#", area);
}
else if ([current_element isEqualToString:#"element b"])
{
NSString *tag = [NSString stringWithFormat:#"</%#>", elementName];
[area appendString:tag];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
if ([current_element isEqualToString:#"element b"]) {
[area appendString:string];
}
}