Parsing XML with a lot of children nodes to UITableView - iphone

I have an xml file that looks like this:
<result>
<trip duration="03:30">
<takeoff date="2010-06-19" time="18:40" city="Moscow"/>
<landing date="2010-06-19" time="20:10" city="Novgorod"/>
<flight carrier="Rossiya" number="8395" eq="320"/>
<price>13429.00</price>
</trip>
<trip duration="03:40">
<takeoff date="2010-06-19" time="09:20" city="Omsk"/>
<landing date="2010-06-19" time="11:15" city="Paris"/>
<flight carrier="AirFrance" number="1145" eq="320"/>
<price>13229.00</price>
</trip>
<trip duration="03:50">
<takeoff date="2010-06-19" time="07:20" city="Omsk"/>
<landing date="2010-06-19" time="14:15" city="Barcelona"/>
<flight carrier="AirFrance" number="1100" eq="320"/>
<price>13329.00</price>
</trip>
</result>
I'd like to get all the parameters and place them in 3 UITableViewCells. As you can see there are 3 flights and the data about them.
I've been trying to parse it in a TableView but I only managed to get the <price> thing going.
How would you deal with parsing complex-structured parameters within an XML file? I mean how would I get takeoff date parameter and so on and so forth?
As far as I could get you can't apply the tactics I'm using like this one:
- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementname isEqualToString:#"price"])
{
currentTweet.dateCreated = currentNodeContent;
}
Any help would be highly appreciated. Thanks in advance.

This is the code that I frequently use to build a dictionary from an XML file that follows this basic model of some well defined repeating element (in this case, "trip"), and a series of data elements within that, some of which I'm reading the attributes of the tag (in this case, "takeoff", "landing" and "flight"), and others I'm reading the data between the opening and closing tags (in this case, just "price").
I have the following ivars:
#interface XmlParserViewController () <NSXMLParserDelegate>
{
NSMutableArray *trips;
NSMutableDictionary *currentTrip;
NSMutableString *currentElement;
}
#end
And then the code looks like:
- (void)viewDidLoad
{
[super viewDidLoad];
trips = [[NSMutableArray alloc] init];
// I'm getting my xml from my bundle. You get it however you're currently getting it.
NSString *filename = [[NSBundle mainBundle] pathForResource:#"results" ofType:#"xml"];
NSData *data = [NSData dataWithContentsOfFile:filename];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
[parser parse];
}
#pragma mark - NSXMLParserDelegate methods
#define kRowElementTag #"trip"
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
NSArray *attributeElementNames = #[#"takeoff", #"landing", #"flight"];
NSArray *foundCharacterElementNames = #[#"price"];
if ([elementName isEqualToString:kRowElementTag])
{
currentTrip = [[NSMutableDictionary alloc] init];
[trips addObject:currentTrip];
if (attributeDict)
[currentTrip setObject:attributeDict forKey:elementName];
}
else if (currentTrip)
{
if ([attributeElementNames containsObject:elementName])
{
if (attributeDict)
[currentTrip setObject:attributeDict forKey:elementName];
}
else if ([foundCharacterElementNames containsObject:elementName] && currentElement == nil)
{
// you can change this to just grab a few fields ... add whatever fields you want to this
currentElement = [[NSMutableString alloc] init];
[currentTrip setObject:currentElement forKey:elementName];
}
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:kRowElementTag])
{
currentTrip = nil;
}
else if (currentElement)
{
currentElement = nil;
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if (currentElement)
{
[currentElement appendString:string];
}
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
NSLog(#"%s error=%#", __FUNCTION__, parseError);
// we should handle the error here
}
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
NSLog(#"%s trips=%#", __FUNCTION__, trips);
// generally I immediately kick off the reload of the table, but maybe
// you want to grok the trips dictionary first.
//
// [self.tableView reloadData];
}
As you can guess, I'm trying to end up with that sort of nested array/dictionary structure that we've gotten used to parsing JSON files. Clearly, I don't like the fact that I have to identify some of the structure of the XML file up front in my code (the fact that the outer array has "trip" tags, that "takeoff", "landing", and "flight" have attributes but "price"` doesn't), etc. But this is a little better than my first attempts at XML parsing that hardcoded values all over the place. Sigh.

You have to manage didStartElement too. This is a sample metacode could be good for you:
-(void)parser:(NSXMLParser*)parser didStartElement:(NSString *)elementName namespaceURI:(NSString*)namespaceURI qualifiedName:(NSString*)qualifiedName attributes:(NSDictionary*)attributeDict {
if([elementName isEqualToString:#"trip"]) {
currentTweet.tripDuration = [attributeDict objectForKey:#"duration"];
} else if ([elementName isEqualToString:#"takeoff"]) {
currentTweet.takeoffDate = [attributeDict objectForKey:#"date"];
currentTweet.takeoffTime = [attributeDict objectForKey:#"time"];
currentTweet.takeoffCity = [attributeDict objectForKey:#"city"];
} else if ([elementName isEqualToString:#"landing"]) {
...............
} else if ...........
}

Related

HTML code disappears in String iOS

I am parsing XML file with RSS from an URL. In the XML, there is a tag as description.
<description>
<![CDATA[
<img width="150" height="150" src="http://www.ipadia.co/wp-content/uploads/2012/02/sayfada-bul-150x150.png" class="attachment-thumbnail wp-post-image" alt="sayfada bul" title="sayfada bul" />Yine çok kişinin dikkatinden kaçan ama faydalı bir araç Bulunduğunuz sayfada arama yapmak bazen çok gerekli olabiliyor Bunun için google arama tabınıkullanacağız Google aramatab ına tıklayin Resimdeki gibi Arama listesinin sonunda bu sayfada bul çıkacak Arama yapmak istediğiniz kelimeyi yazın … Continue reading <span class="meta-nav">→</span>
]]>
</description>
I am using this code as NSString. As you see, there is an image link at the beginning of description tag. I want to take the image link from this string. But when I parse this, It is giving me just the text, not HTML code. When I write this with NSLog, I can't see the image link in it. In short, Xcode doesn't give me the HTML code in the string.
I tried to load this code in UIWebView assuming that it can show the image. But the image didn't be shown in WebView either. Just the text was displayed.
When I look the RSS in browser, I can see the image code in it, but in Xcode, I can't see HTML code, it just gives me the plain text. How can I solve this problem?
Thank you very much.
EDIT: I think the CDATA may cause the issue. How can I challenge with that?
These are my XMLParser methods.
-(void) parser: (NSXMLParser *) parser didStartElement: (NSString *) elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if([elementName isEqualToString:#"channel"])
if (!ItemA)
{
ItemA = [[NSMutableArray alloc] init];
}
if([elementName isEqualToString:#"item"]){
// NSLog(#"ITEM OLUŞTURULDU.");
anItem= [[Item alloc] init];
}
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if (!currentElementValue)
currentElementValue = [[NSMutableString alloc] initWithString:string];
else
[currentElementValue appendString:string];
// NSLog(#"PRCOSSING VALUE :%#", currentElementValue);
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if([elementName isEqualToString:#"channel"])
return;
if ([elementName isEqualToString:#"item"])
{
// NSLog(#"qqqqqq:%#", anItem.title);
[ItemA addObject:anItem];
counter++;
// NSLog(#"kkk:%i", ItemA.count);
// NSLog(#" -*-*-*-*-* COUNTER: %d",counter);
anItem = nil;
return;
}
if ([elementName isEqualToString:#"title"])
{
anItem.title=currentElementValue;
// NSLog(#"kkk-title:%#", anItem.title);
}
if ([elementName isEqualToString:#"description"])
{
anItem.description=currentElementValue;
// NSLog(#"kkkkkk-desc:%#", anItem.description);
}
if ([elementName isEqualToString:#"link"])
{
anItem.link=currentElementValue;
// NSLog(#"kkkkkk-link:%#", anItem.link);
}
if ([elementName isEqualToString:#"pubDate"])
{
anItem.pubDate=currentElementValue;
// NSLog(#"kkkkkk-pubdate:%#", anItem.pubDate);
}
if ([elementName isEqualToString:#"category"])
{
anItem.category=currentElementValue;
// NSLog(#"kkkkkk-category:%#", anItem.category);
}
currentElementValue = nil;
}

NSMutableArray and NSDictionary using for parsed xml data

Please anybody help me.i am in great trouble....Now my Problem is...
Using xml parser i have parsed some attribute and value of the attribute.
using this i am getting the element name:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes: (NSDictionary *)attributeDict
Now using this i am getting value of the attribute from the xml:
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
NSLog(#"found characters: %# %#", currentElement,string);
suppose my xml data are something like that...
-<list>
−<ProductData HASH="838557843">
<id>1</id>
<productNumber>a91cc0f4c7</productNumber>
<name>Product 1</name>
<seoTitle>product-1</seoTitle>
<viewCount>0</viewCount>
<lowStock>0.0</lowStock>
<image>5e928bbae358c93caedf6115fa7d178b.jpg</image>
<dateCreated>2011-10-06T16:08:45</dateCreated>
<dateUpdated>2011-10-06T10:08:45</dateUpdated>
<isDummy>true</isDummy>
<isInventory>false</isInventory>
</ProductData>
-<ProductData HASH="681596439">
<id>2</id>
<productNumber>d8287e2e51</productNumber>
<name>Product 2</name>
<seoTitle>product-2</seoTitle>
−<description>
<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit,....</p>
</description>
<viewCount>0</viewCount>
<availableStock>100.0</availableStock>
<lowStock>0.0</lowStock>
<image>8bbd8dfff3cdd28285d07810a4fe7c32.jpg</image>
<basePrice>10.0</basePrice>
<costPrice>10.0</costPrice>
<height>1.0</height>
<width>1.0</width>
<depth>1.0</depth>
<weight>3.0</weight>
<status>A</status>
<quantityOrderMin>1.0</quantityOrderMin>
<productIsCall>false</productIsCall>
<quantityOrderMax>20.0</quantityOrderMax>
<priceByAttribute>false</priceByAttribute>
<dateCreated>2011-10-06T16:08:45</dateCreated>
<dateUpdated>2011-10-06T10:08:45</dateUpdated>
<isDummy>true</isDummy>
<isInventory>false</isInventory>
</ProductData>
</list>`
Now when i will get the id attribute then it will store in an array then all of the attribute after first id until next id i want to store the attribute and its value in the dictionary,when i will get second id then i want to continue previous process...then according to the value of id i want to display the value in a UIView or UITableView. its not neccesary where i want to display it.
My question is how can i store data in array and dictionary and display it any time in my viewcontroller. Please help me. its becoming a great trouble for me. if you can please give me a sample code as an example. please please anybody help me...
Thanks in Advance
If you change your XML to
<list>
<Object>
<id>1</id>
<product>name</product>
<image>imagename.jpg</image>
<dateCreated>date</dateCreated>
<productleft>10</productleft>
</Object>
<Object>
<id>2</id>
<product>name</product>
<image>imagename.jpg</image>
<dateCreated>date</dateCreated>
<productleft>30</productleft>
</Object>
</list>
Or if your XML looks something like above its easy to use SAX parser ( NSXMLParser you've used ). Otherwise if you want to stick to XML you have posted use DOM style parsing.
Here is the code for the XML after formatting using NSXMLParser.
// these are ivars
NSMutableArray * objectsArray;
NSMutableDictionary * productDict;
NSMutableString * currentString;
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
if([elementName isEqualToString:#"Object"])
{
objectsArray = [[NSMutableArray alloc] init];
productDict = [[NSMutableDictionary 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:#"id"])
{
[productDict setObject:currentString forKey:#"id"];
[currentString release],currentString = nil;
return;
}
if([elementName isEqualToString:#"product"])
{
[productDict setObject:currentString forKey:#"product"];
[currentString release],currentString = nil;
return;
}
if([elementName isEqualToString:#"image"])
{
[productDict setObject:currentString forKey:#"image"];
[currentString release],currentString = nil;
return;
}
if([elementName isEqualToString:#"dateCreated"])
{
[productDict setObject:currentString forKey:#"dateCreated"];
[currentString release],currentString = nil;
return;
}
if([elementName isEqualToString:#"productleft"])
{
[productDict setObject:currentString forKey:#"productleft"];
[currentString release],currentString = nil;
return;
}
if([elementName isEqualToString:#"Object"])
{
[objectsArray addObject:productDict];
[productDict release],productDict = nil;
}
[currentString release], currentString = nil;
}

xcode/iphone custom class xml parcer

I have a custom class and I set specific properties of that class based on the xml node name coming in.
if([elementName isEqualToString:#"strap"])
{
parcelObj.strap = nodeContent;
}
else if([elementName isEqualToString:#"owner"])
{
parcelObj.owner=nodeContent;
}
etc. etc...
I wanna know if I can lose the else if's all together and do this (somehow)
parcelObj."elementName" = nodeContent;
Make sense? I am not sure how to pass the "elementName" as the property ya know?
This may be a bit more info then you are looking for, but check out my custom xml parser JHXMLParser class. It parses an xml file and creates an array of dictionaries. Each dictionary has the xml tags name as a key for the text value inside the tag. So, if the xml data looked like this:
<PHOTOS>
<PHOTO>
<ID>177</ID>
<THUMB><![CDATA[http://www.blah.com/thumbs/489650795.jpg]]></THUMB>
<IMAGE><![CDATA[http://www.blah.com/images/489650795.jpg]]></IMAGE>
</PHOTO>
<PHOTO>
<ID>178</ID>
<THUMB><![CDATA[http://www.blah.com/thumbs/489650798.jpg]]></THUMB>
<IMAGE><![CDATA[http://www.blah.com/images/489650798.jpg]]></IMAGE>
</PHOTO>
</PHOTOS>
It would create an array with 2 dictionary elements. The first dictionary would have 3 keys ("ID", "THUMB", "IMAGE") and 3 values ("117", "http://www.blah.com/thumbs/489650795.jpg", and "http://www.blah.com/images/489650795.jpg") respectively.
Here are the important methods (_key, _previousTag, _currentTag, _elementText, and _parsedData are ivars and _key is a string that would be #"PHOTO" for the above XML):
- (void)parserDidStartDocument:(NSXMLParser *)parser {
_parsedData = [[NSMutableArray alloc] init];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict {
_currentTag = [[NSString alloc] initWithString:elementName];
if ([elementName isEqualToString:_key]) {
NSMutableDictionary *tmpDict = [[NSMutableDictionary alloc] init];
[_parsedData addObject:tmpDict];
[tmpDict release];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if ([_previousTag isEqualToString:_currentTag]) {
[_elementText appendString:[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
} else {
_elementText = [[NSMutableString alloc] initWithString:[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
}
if (![_previousTag isEqualToString:_currentTag]) {
_previousTag = [[NSString alloc] initWithString:_currentTag];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([_previousTag isEqualToString:elementName]) {
[[_parsedData lastObject] setObject:_elementText forKey:elementName];
} else {
_previousTag = [[NSString alloc] initWithFormat:#""];
}
}

NSXML parsing loops 3 times over 1 node

I'm using NSXMLParser to parse an XML file. I'm doing this several times in my app for different purposes. I have one which parses annotations for a store locator, one that parses al the movies from a certain YouTube channel and the last one parses a self made XML file.
All parsers work great except the last one. For some kind of reason it parses i.e. the first node good, but then overwrites it.
Here is my parser code:
//Standard function parser: reading open tag
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict{
currentElement = [elementName copy];
if ([elementName isEqualToString:#"item"]) {
xmlArray = [[NSMutableDictionary alloc] init];
}
}
//Standard function parser: reading string
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
if ([currentElement isEqualToString:#"created_time"]){
[xmlArray setObject:string forKey:currentElement];
NSLog(#"test 1 %#", string);}
}
//Standard function parser: reading close tag
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName{
if ([elementName isEqualToString:#"item"]) {
Post *newPost = [[Post alloc] init];
NSLog(#"test %#", xmlArray );
newPost.created_time = [xmlArray objectForKey:#"created_time"];
[containerArray addObject:newPost];
[xmlArray release];
}
}
The XML looks like this:
<?xml version="1.0" encoding="UTF-8" ?>
<items>
<item>
<created_time>test</created_time>
<message>blabla</message>
<picture>http://www.trentshow.com/images/tada.jpg</picture>
</item>
</items>
And the log outputs the following:
2011-06-10 10:43:24.446 TabbedCalculation[1502:207] test 1 test
2011-06-10 10:43:24.448 TabbedCalculation[1502:207] test 1
2011-06-10 10:43:24.449 TabbedCalculation[1502:207] test 1
2011-06-10 10:43:24.450 TabbedCalculation[1502:207] test {
"created_time" = "\n";
}
I'm not getting why the parser loops 3 times over the created_time node. Any help would be great!
Try by following solution -- should work.
foundCharacters can be called for multiple time for each key value --- basically you should append all found character list.
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
if ([currentElement isEqualToString:#"created_time"]){
NSString *valueKey = [xmlArray ValueForKey:forKey:currentElement];
if(nil != valueKey)
{
valueKey = [valueKey stringByAppendingString:string];
}else
{
valueKey = string;
}
[xmlArray setObject:valueKey forKey:currentElement];
NSLog(#"test 1 %#", string);}
}

NSXMLParser problem - SeismicXML Sample

Hey,
I have to parse XML in my iOS app. I took Apple's SeismicXML Sample as my base, but I'm experiencing a really strange behaviour.
These are my parser methodes:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict {
if ([elementName isEqualToString:kEntryElementName]) {
Photo *photo = [[Photo alloc] init];
self.currentPhotoObject = photo;
[photo release];
} else if ([elementName isEqualToString:kTitleElementName] ||
[elementName isEqualToString:kLocationElementName] ||
[elementName isEqualToString:kAuthorElementName]) {
accumulatingParsedCharacterData = YES;
[currentParsedCharacterData setString:#""];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:kEntryElementName]) {
NSLog(#"Did End - Titel:%#", self.currentPhotoObject.titleText);
NSLog(#"Did End - Location:%#", self.currentPhotoObject.locationText);
NSLog(#"Did End - Author:%#", self.currentPhotoObject.author);
[self.currentParseBatch addObject:self.currentPhotoObject];
parsedPhotosCounter++;
if ([self.currentParseBatch count] >= kMaximumNumberOfPhotosToParse) {
[self performSelectorOnMainThread:#selector(addPhotosToList:)
withObject:self.currentParseBatch
waitUntilDone:NO];
self.currentParseBatch = [NSMutableArray array];
}
}
else if ([elementName isEqualToString:kTitleElementName]) {
self.currentPhotoObject.titleText = self.currentParsedCharacterData;
}
else if ([elementName isEqualToString:kAuthorElementName]) {
self.currentPhotoObject.author = self.currentParsedCharacterData;
}
else if ([elementName isEqualToString:kLocationElementName]) {
self.currentPhotoObject.locationText = self.currentParsedCharacterData;
}
accumulatingParsedCharacterData = NO;
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if (accumulatingParsedCharacterData) {
// If the current element is one whose content we care about, append 'string'
// to the property that holds the content of the current element.
//
[self.currentParsedCharacterData appendString:string];
}
}
Everything works great, the XML Data comes correctly. The parser parses everything as it should.
The problem is in the parser didEndElement methode.
else if ([elementName isEqualToString:kTitleElementName]) {
self.currentPhotoObject.titleText = self.currentParsedCharacterData;
}
When I get "self.currentPhotoObject.titleText" via NSLog, I get the right parsed Data. But then:
else if ([elementName isEqualToString:kAuthorElementName]) {
self.currentPhotoObject.author = self.currentParsedCharacterData;
}
When I get the NSLog of "self.currentPhotoObject.titleText" and from "self.currentPhotoObject.author" here, both give me the author.
In the third parsed methode it is the same. All three properties (titleText, author and locationText) are the locationText.
I have no idea why .titleText e.g. is changed when the parser sets .author.
I have doublechecked everything at least 10 times and compared it to the SeismicXML sample but I can't find the problem.
Please help me. I'm thankfull for every hint !
Greets Sebastian
ps: My properties in the .m file:
#interface ParseOperation () <NSXMLParserDelegate>
#property (nonatomic, retain) Photo *currentPhotoObject;
#property (nonatomic, retain) NSMutableArray *currentParseBatch;
#property (nonatomic, retain) NSMutableString *currentParsedCharacterData;
#end
#implementation ParseOperation
#synthesize photoData, currentPhotoObject, currentParsedCharacterData, currentParseBatch;
It's because you assign same NSMutableString instance to all this properties.
1) Declare author, titleText, locationText properties as copy to avoid this in future.
2) Make a copy each time you want to return value of NSMutableString or assign it to something
self.currentPhotoObject.titleText = [[self.currentParsedCharacterData copy] autorelease];