XML Parsing issue in Xcode? - objective-c

I am making a mac application, and I need it to parse a local xml file and display it into a tableView. For some reason, I get a blank row in my tableView, which makes no sense, as it has found characters that are in my xml. Here is my code:
- (void)parserDidStartDocument:(NSXMLParser *)parser{
NSLog(#"found file and started parsing");
}
- (void)parseXMLFileAtURL:(NSString *)URL
{
//you must then convert the path to a proper NSURL or it won't work
NSURL *xmlURL = [NSURL URLWithString:URL];
// here, for some reason you have to use NSClassFromString when trying to alloc NSXMLParser, otherwise you will get an object not found error
// this may be necessary only for the toolchain
rssParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];
// Set self as the delegate of the parser so that it will receive the parser delegate methods callbacks.
[rssParser setDelegate: self];
// Depending on the XML document you're parsing, you may want to enable these features of NSXMLParser.
[rssParser setShouldProcessNamespaces:NO];
[rssParser setShouldReportNamespacePrefixes:NO];
[rssParser setShouldResolveExternalEntities:NO];
[rssParser parse];
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
NSString * errorString = [NSString stringWithFormat:#"Unable to download XML feed (Error code %i )", [parseError code]];
NSLog(#"Error parsing XML: %#", errorString);
}
- (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:#"event"]) {
// clear out our story item caches...
item = [[NSMutableDictionary alloc] init];
date = [[NSMutableString alloc] init];
opponent = [[NSMutableString alloc] init];
location = [[NSMutableString alloc] init];
time = [[NSMutableString alloc] init];
games = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
NSLog(#"ended element: %#", elementName);
if ([elementName isEqualToString:#"event"]) {
// save values to an item, then store that item into the array...
[item setObject:date forKey:#"date"];
[item setObject:opponent forKey:#"opponent"];
[item setObject:location forKey:#"location"];
[item setObject:time forKey:#"time"];
[item setObject:games forKey:#"games"];
NSMutableArray *stories = [[NSMutableArray alloc] init];
[stories addObject:[item copy]];
[arrayController addObject:[NSDictionary dictionaryWithObjectsAndKeys:
date, #"date",
opponent, #"opponent",
location, #"location",
time, #"time",
games, #"games"
, nil]];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
//NSLog(#"found characters: %#", string);
// save the characters for the current item...
if ([date isEqualToString:#"date"]) {
[date appendString:string];
} else if ([opponent isEqualToString:#"opponent"]) {
[opponent appendString:string];
} else if ([location isEqualToString:#"location"]) {
[location appendString:string];
} else if ([time isEqualToString:#"time"]) {
[time appendString:string];
} else if ([games isEqualToString:#"games"]) {
[games appendString:string];
}
}
- (void)parserDidEndDocument:(NSXMLParser *)parser {
NSLog(#"all done!");
[tabelview reloadData];
}
When i remove my adding part, where it adds item to the arraycontroller, and add
[arrayController addObject:stories]; I get a buch of ('s
If there is anything else you need, do not just down-vote, and instead tell me. Thanks!
Here is my xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xmlData>
<event>
<date>date here</date>
<opponent>opponent here</opponent>
<location>location here</location>
<time>time here</time>
<games>games here</games>
</event>
<event>
<date>date here</date>
<opponent>opponent here</opponent>
<location>location here</location>
<time>time here</time>
<games>games here</games>
</event>
<event>
<date>date here</date>
<opponent>opponent here</opponent>
<location>location here</location>
<time>time here</time>
<games>games here</games>
</event>
<event>
<date>date here</date>
<opponent>opponent here</opponent>
<location>location here</location>
<time>time here</time>
<games>games here</games>
</event>
</xmlData>

The error is in your parser. Please revise the logic. You are not using your object item when filling your table view array. Also, you are not catching the text in between the XML elements and are not assigning them to the appropriate variables.
Note the following:
When you enter an element, keep track of which element you are in currently
When you find characters you have to fill the appropriate attribute variable according to which element you are in
When you finish the event element, you should add your item with all its filled in keys into your data array.

You need to know how to use array. You are allocating one array stories. But then not using. Please Check your didEndElement method.
Make one class of Event, create .h and .m file and then create properties of your all element and then add whole object of Event class into an array. That array you can in appHandler or Single ton class.
Check this thing. May it help you.

Related

Parsing XML into NSManagedObjects using categories and what to do with properties in categories?

Based on the excellent example "Parsing XML with NSXMLParser" in the book "The Big Nerd Ranch Guide" (3rd ed.), I haved added categories to my NSManagedObjects for which I want to add XML parsing. These categories provide only parsing functionality.
This is how I have implemented these categories:
.h:
#import "IBCompany.h"
#interface IBCompany (Xml) <NSXMLParserDelegate>
- (void)parseXmlString:(NSString*)xmlStr withCompletion:(void(^)(NSError *error))completionBlock;
#end
.m:
#implementation IBCompany (Xml)
- (void)parseXmlString:(NSString*)xmlStr withCompletion:(void(^)(NSError *error))completionBlock;
{
NSData *xmlData = [xmlStr dataUsingEncoding:NSUTF8StringEncoding];
NSXMLParser *parser = [[NSXMLParser alloc]initWithData:xmlData];
parser.delegate = self;
[parser parse];
xmlData = nil;
NSError *error;
completionBlock(error);
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqualToString:#"Issue"]) {
IBIssue *issue = [NSEntityDescription insertNewObjectForEntityForName:#"IBIssue" inManagedObjectContext:self.managedObjectContext];
issue.company = self;
issue.parentParserDelegate = self;
parser.delegate = issue;
}
As you can see in this code snippet, I switch the parser delegate to other subclasses / XML child elements to have them further process the next XML elements, which belong to them until the end of the XML element is reached and the delegate is set back to the parent.
This is why I need to store the parent delegate in the child. However, ivars and properties are not allowed in categories.
I came up with this solution which seems to circumvent this problem:
Child element, h:
#import "IBIssue.h"
#interface IBIssue (Xml) <NSXMLParserDelegate>
#property id parentParserDelegate;
#end
#import "IBIssue+Xml.h"
#implementation IBIssue (Xml)
NSMutableString *currentString;
NSString *currentXmlDocument;
id _parentParserDelegate;
- (id)parentParserDelegate
{
return _parentParserDelegate;
}
- (void)setParentParserDelegate:(id)parentParserDelegate;
{
_parentParserDelegate = parentParserDelegate;
}
- (NSDateFormatter*)dateFormatter
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"]];
[dateFormatter setDateFormat:#"yyy-MM-dd"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT: 0]];
return dateFormatter;
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqualToString:#"IssueID"]) {
currentString = [[NSMutableString alloc]init];
if ([attributeDict[#"Type"] isEqualToString:#"Ticker"]) self.ticker = currentString;
else if ([attributeDict[#"Type"] isEqualToString:#"Name"]) self.issueName = currentString;
else if ([attributeDict[#"Type"] isEqualToString:#"CUSIP"]) self.cusip = currentString;
else if ([attributeDict[#"Type"] isEqualToString:#"ISIN"]) self.isin = currentString;
else if ([attributeDict[#"Type"] isEqualToString:#"RIC"]) self.ric = currentString;
else if ([attributeDict[#"Type"] isEqualToString:#"SEDOL"]) self.sedol = currentString;
else if ([attributeDict[#"Type"] isEqualToString:#"DisplayRIC"]) self.displayRic = currentString;
else if ([attributeDict[#"Type"] isEqualToString:#"InstrumentPI"]) ; //
else if ([attributeDict[#"Type"] isEqualToString:#"QuotePI"]) ; //
} else if ([elementName isEqualToString:#"Exchange"]) {
currentString = [[NSMutableString alloc]init];
self.exchangeCode = attributeDict[#"Code"];
self.exchangeCountry = attributeDict[#"Country"];
self.exchange = currentString;
} else if ([elementName isEqualToString:#"MostRecentSplit"]) {
currentString = [[NSMutableString alloc]init];
self.mostRecentSplitDate = [self.dateFormatter dateFromString:attributeDict[#"Date"]];
// self.mostRecentSplit = [NSNumber numberWithFloat: currentString.floatValue];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
// NSLog(#"appendString: %#", string);
[currentString appendString:string];
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:#"Issue"]) {
parser.delegate = self.parentParserDelegate;
} else if ([elementName isEqualToString:#"MostRecentSplit"]) {
self.mostRecentSplit = [NSNumber numberWithFloat: currentString.floatValue];
}
currentString = nil;
}
#end
I save the delegate to the parent in a variable _parentDelegate which is declared outside the ivar declaration block and does not seem to be a real ivar.
This code works well in my tests and I wonder if I missed something which will turn out to become a problem later in the development process or if this design is ok.
What are your thoughts on that?
Thank you!
I'm not sure how the compiler will treat that variable. Could it be allocated so that only one variable is shared by all objects of this type? If your XML is parsed such that more than one IBCompany exists at a point in time it could cause a problem. I'd write a test that allocated two IBCompany objects, cause them both to write different values to _parentDelegate, then assert the values are different.
Or ignore the issue if there is no possibility that two IBCompany objects are parsed in parallel. You'd have to ensure that the XML can't have an IBCompany inside another IBCompany, multiple parts of the XML will not be processed in parallel, and that multiple XML documents will not be processed in parallel.
I don't see the need for a category. Categories are useful when you shouldn't write a subclass to an existing class, such as adding functionality to classes in the Cocoa framework. You are writing a custom subclass, so why not add the ivar to your subclass? You can have additional ivars in managed objects that are not saved in the Core Data backing stores. At most I'd just use an extension to segregate XML parsing code from the rest of the managed object.

iOS: Parsing a xml from HTTP using NSXMLParser for

It's first time using NSXMLParser and wondering if you give me some direction of parsing the returned xml from an http request that looks like:
<?xml version="1.0" ?>
<theresponse>
<status>OK</status>
<pricing currency="USD" symbol="$">
<price class="items">24.00</price>
<price class="shipping">6.00</price>
<price class="tax">1.57</price>
</pricing>
</theresponse>
I know the basic of parsing delegate methods, I just want to know what the code would look like in didEndElement/foundCharacters/didStartElement for retreiving above items(currency/items/shipping/tax)? any help greatly appreciated.
This is a little more tricky than some standard NSXMLParser code; Because essentially when you are looking for the "shipping" you want "6.00" but those two pieces of data are returned to you in different delegate methods, which is normal. But usually the element would be named "shipping" so in parser:didEndElement:namespaceURI:qualifiedName: you would automatically have the element name as it was passed into the method.
The solution would seem simple, have a _currentAttributes ivar and in parser:didStartElement:namespaceURI:qualifiedName:attributes: do something like _currentAttributes = attributeDict; and then handle this in the didEndElement: method. However this style would easily break, even on this moderately simple XML.
My way of handling this would be to store the attributes dictionary passed into the didStartElement: and set it in a dictionary as the object for the key of the element name. Combining this style with the standard use of an NSMutableString as a characterBuffer of sorts allows you to put all of your logic into the didEndElement: method.
Side note: I am also quite fond of having my NSXMLParserDelegate classes be NSXMLParser subclasses, as this one is. However the delegate methods would be identical if it were not.
ItemParser.h
#import <Foundation/Foundation.h>
#interface ItemParser : NSXMLParser <NSXMLParserDelegate>
#property (readonly) NSDictionary *itemData;
#end
ItemParser.m
#import "ItemParser.h"
#implementation ItemParser {
NSMutableDictionary *_itemData;
NSMutableDictionary *_attributesByElement;
NSMutableString *_elementString;
}
-(NSDictionary *)itemData{
return [_itemData copy];
}
-(void)parserDidStartDocument:(NSXMLParser *)parser{
_itemData = [[NSMutableDictionary alloc] init];
_attributesByElement = [[NSMutableDictionary alloc] init];
_elementString = [[NSMutableString alloc] init];
}
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
// Save the attributes for later.
if (attributeDict) [_attributesByElement setObject:attributeDict forKey:elementName];
// Make sure the elementString is blank and ready to find characters
[_elementString setString:#""];
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
// Save foundCharacters for later
[_elementString appendString:string];
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if ([elementName isEqualToString:#"status"]){
// Element status only contains a string i.e. "OK"
// Simply set a copy of the element value string in the itemData dictionary
[_itemData setObject:[_elementString copy] forKey:elementName];
} else if ([elementName isEqualToString:#"pricing"]) {
// Pricing has an interesting attributes dictionary
// So copy the entries to the item data
NSDictionary *attributes = [_attributesByElement objectForKey:#"pricing"];
[_itemData addEntriesFromDictionary:attributes];
} else if ([elementName isEqualToString:#"price"]) {
// The element price occurs multiple times.
// The meaningful designation occurs in the "class" attribute.
NSString *class = [[_attributesByElement objectForKey:elementName] objectForKey:#"class"];
if (class) [_itemData setObject:[_elementString copy] forKey:class];
}
[_attributesByElement removeObjectForKey:elementName];
[_elementString setString:#""];
}
-(void)parserDidEndDocument:(NSXMLParser *)parser{
_attributesByElement = nil;
_elementString = nil;
}
-(void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError{
NSLog(#"%# with error %#",NSStringFromSelector(_cmd),parseError.localizedDescription);
}
-(BOOL)parse{
self.delegate = self;
return [super parse];
}
#end
And so to test I stored the XML you posted above into a file named "ItemXML.xml". And tested it using this code:
NSURL *url = [[NSBundle mainBundle] URLForResource:#"ItemXML" withExtension:#"xml"];
ItemParser *parser = [[ItemParser alloc] initWithContentsOfURL:url];
[parser parse];
NSLog(#"%#",parser.itemData);
The result I got was:
{
currency = USD;
items = "24.00";
shipping = "6.00";
status = OK;
symbol = "$";
tax = "1.57";
}

String Replacement operations in objective c

I got the result which is in the form of
<ZmaterialGroupList><Matkl>001</Matkl><Text>Metal processing</Text></ZmaterialGroupList>
I need to get the result as 001 Metal processing when i apply the string replacement function upon this it give an exception.Please help me.
Added code from comment:
for(int i=0; i<[soapArray.items count]; i++) {
NSString *str = [soapArray.items objectAtIndex:i];
str = [str stringByReplacingOccurrencesOfString:#"<Matkl>" withString:#""];
}
In this way I wrote but I got an exception like
Invalid argument pass at str
You might do better to use NSXMLParser rather than trying to replace pieces of the XML.
If you update your question to include a bit more explaining your code (specifically the code that deals with soapArray), I should be able explain a bit more as to why your code doesn't work as it is.
Using NSXMLParser
It's important to remember that NSXMLParser just reads the data you give it sequentially, it doesn't use a DOM structure.
Setup
Primarily you need to give your parser something to parse! In my example I get a resource from the bundle and convert it to NSData. There is also however another option to initWithContentsOfURL. Make sure you don't forget to set your delegate!
-(void) parse
{
NSString *file = #"myXMLFile.xml";
NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[file stringByDeletingPathExtension] ofType:[file pathExtension]]];
//If you already have a string:
//NSData* data=[xmlString dataUsingEncoding:NSUTF8StringEncoding];
if (data.length == 0)
{
//No data
return nil;
}
...
}
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
[parser parse];
Parsing
-(void) parserDidStartDocument:(NSXMLParser *)parser
{
//Here you set up any variables you might need while parsing
}
-(void) parserDidEndDocument:(NSXMLParser *)parser
{
//I usually don't have to do anything here but you might need to release some variables here for example.
}
-(void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
//Here is where the bulk of the parsing is done if you are using attributes. I prefer to use attributes as it looks cleaner both in the XML file and the parser.
if ([elementName isEqualToString:#"element1"])
{
//Just an example of what you might want to do
int index = [[attributeDict valueForKey:#"attribute1"] intValue];
NSString *name = [attributeDict valueForKey:#"n"];
[exampleDictionary setValue:name forKey:[NSString stringWithFormat:#"%d", index]];
}
if ([elementName isEqualToString:#"element2"])
{
//We need to know that the next piece of information (from foundCharacters) is for element 2
currentElement = ELEMENT_2;
}
}
//If you haven't used attributes you might find that you have a lot of parsing to do here instead.
-(void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
//Check what this info is for?
if(currentElement == ELEMENT_2)
{
element2Data = [NSString stringWithString:string];
}
}
-(void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:#"element2"])
{
myObject.somethingThatNeedsElement2 = element2;
}
}
Finishing
After finishing it's a good idea to check that nothing went wrong and free up the parser memory. So in your parse method, add this after the call to [parser parse].
if ([parser parserError] != nil)
{
[[[[UIAlertView alloc] initWithTitle:#"Error parsing XML" message:[[parser parserError] localizedDescription] delegate:nil cancelButtonTitle:#"Done" otherButtonTitles:nil] autorelease] show];
}
[parser release];
Consider using an XML parser or you can use NSScanner:
Example:
NSString *wanted;
NSString *fullMessage;
NSString *xml = #"<ZmaterialGroupList><Matkl>001</Matkl><Text>Metal processing</Text></ZmaterialGroupList>";
NSScanner *scanner = [NSScanner scannerWithString:xml];
[scanner scanUpToString:#"<Matkl>" intoString:nil];
[scanner scanString:#"<Matkl>" intoString:nil];
[scanner scanUpToString:#"</Matkl>" intoString:&wanted];
fullMessage = wanted;
[scanner scanUpToString:#"<Text>" intoString:nil];
[scanner scanString:#"<Text>" intoString:nil];
[scanner scanUpToString:#"</Text>" intoString:&wanted];
fullMessage = [fullMessage stringByAppendingFormat:#" %#", wanted];
NSLog(#"fullMessage: '%#'", fullMessage);
NSLog output:
fullMessage: '001 Metal processing'

Parsing simple XML in Objective-C

I have the following very simple XML returned from a webserver which I use ASIHttpRequest to connect to:
<element1>something</element1>
<element2>somethingelse</element2>
<element3>anotherthing</element3>
ASIHttpRequest can return it as NSData or NSString. I need to parse the information, what is the easiest way to do so?
Thanks
There are some XML parsers available for iOS NSXMLParser, libxml2 (DOM and SAX),TBXML,KissXML. You can refer http://www.raywenderlich.com/553/how-to-chose-the-best-xml-parser-for-your-iphone-project to choose best XML Parser (Speed and memory footprint). Easiest would be TBXML. NSXMLParser is easy as well.
NSXMLParser* xmlParser = [[NSXMLParser alloc] initWithData:receivedXMLData];//init NSXMLParser with receivedXMLData
[xmlParser setDelegate:self]; // Set delegate for NSXMLParser
[xmlParser parse]; //Start parsing
[xmlParser release];
//Delegate Methods
//Have a instance variable NSMutableString* currentString; to hold data between elements and NSMutableArray* elementsArray; to hold parsed data
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
elementsArray = [[NSMutableArray alloc] init];
}
//store all found characters between elements in currentString mutable string
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if(!currentString)
{
currentString = [[NSMutableString alloc] init];
}
[currentString appendString:string];
}
//When end of XML tag is found this method gets notified
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if([elementName isEqualToString:#"element1"])
{
[elementsArray addObject:currentString];
[currentString release],currentString=nil;
return;
}
if([elementName isEqualToString:#"element2"])
{
[elementsArray addObject:currentString];
[currentString release],currentString=nil;
return;
}
if([elementName isEqualToString:#"element3"])
{
[elementsArray addObject:currentString];
[currentString release],currentString=nil;
return;
}
[currentString release],currentString =nil;
}
//Parsing has ended
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
NSLog(#"Content of Elements Array: %#",elementsArray);
[elementsArray release],elementsArray=nil;
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
UIAlertView* parseErrorAlert = [[UIAlertView alloc] initWithTitle:#"Parse Error" message:[NString stringWithFormat:"%#",[parseError localizedDescription]] delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[parseErrorAlert show];
[parseErrorAlert release];
}
You have other delegate methods too like parseErrorOccured. Refer http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSXMLParser_Class/Reference/Reference.html
For TBXML : http://tbxml.co.uk/TBXML/API.html
Updated: Implemented parseError delegate method and valid xml example
XML You posted in code is not a valid XML (You can check XML validation online: http://validator.w3.org/#validate_by_input) so it will throw an parseError. Here is valid XML for XML you posted in question:
<?xml version="1.0"?>
<root>
<element1>something</element1>
<element2>somethingelse</element2>
<element3>anotherthing</element3>
</root>
Try also XMLObject. This may helps if you want to keep your cocoa style in your project.

How to handle HTML Strings in Cocoa Touch

I'm using a RSS Reader and works fine when I tap the UITableViewCell to load the <link> either in a UIWebView or to open Safari on that link.
But I really want to learn how to load the Topic content into the application instead showing the entire site or jump to Safari
In the RSS feed per each <item> there is a <body> tag (and a <Description> that contains the same but encoded) that contains the topic content, like the image below shows:
alt text http://www.balexandre.com/temp/2010-04-13_0953.png
So, instead of catching the <link> I'm assigning the <body>. Problem is that it does not work correctly :-(
for this example I only get the content until the first <br> nothing more.
I'm using a NSString as I would use in C#, should I use any other object, is there a better object to use on such data?
Should I use an UITextView to load this information (as it has scroll already) or I should use a UIWebView instead?
Thank you.
added
- (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:#"item"]) {
// clear out our story item caches...
item = [[NSMutableDictionary alloc] init];
currentTitle = [[NSMutableString alloc] init];
currentDate = [[NSMutableString alloc] init];
currentSummary = [[NSMutableString alloc] init];
currentLink = [[NSMutableString alloc] init];
currentBody = [NSMutableString new]; // added by me
currentCreator = [NSMutableString new]; // added by me
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
//NSLog(#"ended element: %#", elementName);
if ([elementName isEqualToString:#"item"]) {
// save values to an item, then store that item into the array...
[item setObject:currentTitle forKey:#"title"];
[item setObject:currentLink forKey:#"link"];
[item setObject:currentSummary forKey:#"summary"];
[item setObject:currentDate forKey:#"date"];
[item setObject:currentBody forKey:#"body"]; // <----
[item setObject:currentCreator forKey:#"dc:creator"];
[stories addObject:[item copy]];
NSLog(#"adding story: %#", currentTitle);
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
//NSLog(#"found characters: %#", string);
// save the characters for the current item...
if ([currentElement isEqualToString:#"title"]) {
[currentTitle appendString:string];
} else if ([currentElement isEqualToString:#"link"]) {
[currentLink appendString:string];
} else if ([currentElement isEqualToString:#"summary"]) {
[currentSummary appendString:string];
} else if ([currentElement isEqualToString:#"pubDate"]) {
[currentDate appendString:string];
} else if ([currentElement isEqualToString:#"body"]) {
[currentBody appendString:string]; // <----
} else if ([currentElement isEqualToString:#"dc:creator"]) {
[currentCreator appendString:string];
}
}
and to pass to my WebBrowser View I have:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic
int storyIndex = [indexPath indexAtPosition: [indexPath length] - 1];
NSString * storyContent = [[stories objectAtIndex: storyIndex] objectForKey: #"body"];
// load web view
[self showWebPage:storyContent]; // <-- Here (storyContent variable) I only have the content until the first <br> :-(
}
Take a look at the SeismicXML sample project from Apple. It gives you a general idea of how to use NSXMLParser, and is also written to parse the XML data in its own thread to allow the user to still interact with the UI. If you are looking for a nested tag (i.e. <body> within <item>), you will want a flag to tell if you are currently within the <item> tag or not. Otherwise you will parse all <body> tags.
To address your second question, it depends on how you want to present this information to the user. If you want the text styled, you will need to use a UIWebView. Otherwise you can strip out what you need and have that spread out through a UITableView, or a custom view you create in Interface Builder.
Edit: After seeing your last comment, you need to create a flag (i.e. insideBody) and check for that inside of foundCharacters:
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
//NSLog(#"found characters: %#", string);
// save the characters for the current item...
if(insideBody == YES) {
[currentBody appendString:string];
} else if ([currentElement isEqualToString:#"title"]) {
[currentTitle appendString:string];
} else if ([currentElement isEqualToString:#"link"]) {
[currentLink appendString:string];
} else if ([currentElement isEqualToString:#"summary"]) {
[currentSummary appendString:string];
} else if ([currentElement isEqualToString:#"pubDate"]) {
[currentDate appendString:string];
} else if ([currentElement isEqualToString:#"dc:creator"]) {
[currentCreator appendString:string];
}
}
You will probably have to do the same in didStartElement: and didEndElement:, as getting data from foundCharacters: will only give you the content of tags and not the tags themselves.
Use NSXMLParser to detect the <body> and </body> tags, and just grab everything between them into a string.
Now that I can see your code, I can see your bug. When you encounter the <br /> tag you change the currentElement to #"br", which means that you stop adding characters to currentBody.