XML data parsing in xcode and storing it - ios

i have a XML file which have 5 items(each have title and url) in it i want retrieve all of them in xcode using xml parsing and then want to store all the entries in arrays so i can use them in application. let me show you all my code.
XML File
<data>
<item>
<title>Eggs</title>
<link>
http://URL/ios/category.php?cat_id=14
</link>
</item>
<item>
<title>Bakery</title>
<link>
http://URL/ios/category.php?cat_id=15
</link>
</item>
<item>
<title>Bread</title>
<link>
http://URL/ios/category.php?cat_id=16
</link>
</item>
<item>
<title>Cakes, Pies Patisserie</title>
<link>
http://URL/ios/category.php?cat_id=17
</link>
</item>
<item>
<title>Specialty Breads</title>
<link>
http://URL/ios/category.php?cat_id=119
</link>
</item>
</data>
Here is my header file
#import "XMLStringFile.h"
#interface MyViewController : UIViewController<NSXMLParserDelegate>{
NSMutableArray *rssOutputData;
NSMutableString *nodecontent;
NSXMLParser *xmlParserObject;
XMLStringFile *xmlStringFileObject;
}
now in xcode here is my viewdidload code
rssOutputData = [[NSMutableArray alloc]init];
NSData *xmlData=[[NSData alloc]initWithContentsOfURL:[NSURL URLWithString:#"http://URL/mycategories.xml"]];
xmlParserObject =[[NSXMLParser alloc]initWithData:xmlData];
[xmlParserObject setDelegate:self];
[xmlParserObject parse];
here is all the parsing methods
#pragma mark NSXMLParser delegate
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
if([elementName isEqualToString:#"item"]){
xmlStringFileObject =[[XMLStringFile alloc]init];
} else {
nodecontent = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
[nodecontent appendString:[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
NSLog(#"node content = %#",nodecontent);
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if([elementName isEqualToString:#"item"]){
[rssOutputData addObject:xmlStringFileObject];
xmlStringFileObject = nil;
} else if([elementName isEqualToString:#"title"]){
xmlStringFileObject.xmltitle= nodecontent;
nodecontent = nil;
}
else if([elementName isEqualToString:#"link"]){
xmlStringFileObject.xmllink= nodecontent;
nodecontent = nil;
}
}
Here is XMLStringFile.h and XMLStringFile.m
XMLStringFile.h
#import <Foundation/Foundation.h>
#interface XMLStringFile : NSObject {
NSString *xmllink,*xmltitle;
}
#property(nonatomic,retain)NSString *xmllink,*xmltitle;
#end
And XMLStringFile.m file
#import "XMLStringFile.h"
#implementation XMLStringFile
#synthesize xmllink,xmltitle;
-(void)dealloc
{
}
Now i want to store my both entries in two separate arrays titles and links and then i want to retrieve them from those arrays in future. I need help please do let me know how can i do this. for now xmltitle and xmllink storing retrieved data but is only nslog last entire if i use it in another function.

Download XMLReader
In ViewDidLoad method.
- (void)viewDidLoad {
NSData *xmlData=[[NSData alloc]initWithContentsOfURL:[NSURL URLWithString:#"http://cms.proxiclients.com/choithrams/mycategories.xml"]];
NSString *XMLString = [[NSString alloc]initWithData:xmlData encoding:NSUTF8StringEncoding];
XMLString = [XMLString stringByReplacingOccurrencesOfString:#"\n" withString:#""];
NSDictionary *dict = [XMLReader dictionaryForXMLString:XMLString error:nil];
NSLog(#"== %#",dict);
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
you can get dictionary value as below.
NSString *linkText = [[[[dict valueForKey:#"data"] valueForKey:#"item"] valueForKey:#"link"] valueForKey:#"text"];
linkText = [linkText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
Your Output is Dictionary:

Related

How can I retrieve the value by parsing my XML file

I'm trying to gather the data from an XML file and add to an array. The data looks something like this..
<Placemark>
<name><![CDATA[Kingsway (Start)]]></name>
<description><![CDATA[]]></description>
<TimeStamp><when>2014-04-30T17:03:28.681Z</when></TimeStamp>
<styleUrl>#start</styleUrl>
<Point>
<coordinates>-1.408894,52.13977,176.0</coordinates>
</Point>
</Placemark>
<Placemark id="tour">
<name><![CDATA[Kingsway]]></name>
<description><![CDATA[]]></description>
<styleUrl>#track</styleUrl>
<ExtendedData>
<Data name="type"><value><![CDATA[running]]></value></Data>
</ExtendedData>
<gx:MultiTrack>
<altitudeMode>absolute</altitudeMode>
<gx:interpolate>1</gx:interpolate>
<gx:Track>
<when>2014-04-30T17:03:28.681Z</when>
<gx:coord>-1.408894 52.13977 176.0</gx:coord>
<when>2014-04-30T17:03:31.071Z</when>
<gx:coord>-1.407839 52.139166 174.0</gx:coord>
<when>2014-04-30T17:03:32.070Z</when>
<gx:coord>-1.407486 52.138963 175.0</gx:coord>
<when>2014-04-30T17:03:33.073Z</when>
<gx:coord>-1.407142 52.138755 174.0</gx:coord>
<when>2014-04-30T17:03:34.073Z</when>
<gx:coord>-1.406805 52.138555 173.0</gx:coord>
<when>2014-04-30T17:03:35.073Z</when>
<gx:coord>-1.40663 52.138441 173.0</gx:coord>
<when>2014-04-30T17:03:36.601Z</when>
<gx:coord>-1.405929 52.138027 172.0</gx:coord>
<when>2014-04-30T17:03:37.601Z</when>
<gx:coord>-1.405574 52.137817 172.0</gx:coord>
I'm only interested in the values following the tag gx:coord, so I'm parsing the file using the code below.
- (void)loadMap:(NSInteger)selJourney
{
NSString *journey = #"KML_Sample";
NSString *path = [[NSBundle mainBundle] pathForResource:journey ofType:#"kml"];
[self parseXMLFile:path];
}
- (void)parseXMLFile:(NSString *)pathToFile
{
NSXMLParser *addressParser;
[myParser setShouldProcessNamespaces:NO];
[myParser setShouldReportNamespacePrefixes:NO];
[myParser setShouldResolveExternalEntities:NO];
NSURL *xmlURL = [NSURL fileURLWithPath:pathToFile];
myParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];
[myParser setDelegate:self];
[myParser setShouldResolveExternalEntities:YES];
[myParser parse];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI: (NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ( [elementName isEqualToString:#"gx:coord"] ) {
polylineList = [[NSMutableArray alloc] init];
}
}
When the parser method runs and it finds the right elementName I want to retrieve the string data after the tag and add it to my NSMutableArray polylineList.
The other parameters in parser all return nil so am I missing something?
I managed to solve this myself using the method below
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if ((addRec) && (![string isEqualToString:#"\n"]))
{
NSArray *tmpArray = [[NSArray alloc] initWithObjects:string, nil];
if (polylineList.count > 0)
[polylineList addObject:string];
else
polylineList = [[NSMutableArray alloc] initWithArray:tmpArray];
}
}
I hope this is helpful to those who are using KML, MapKit or just parsing XML.

Parse XML response in IOS

I am creating an IOS application. In which I have to use SOAP web-service to get some details. So that I have used SUDZ-C to generate the stub. I can able to call web-service and got the response. But I can't parse the response. The below is the XML response.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<ViewAppTrackResponse xmlns="http://service.cmp.app.com">
<ViewAppTrackResponseReturn>
<ns1:monthBO xmlns:ns1="http://response.cmp.app.com">
<monthListItem>
<ns2:date xmlns:ns2="http://bean.cmp.app.com">1-2-2014, Saturday (nonworking day)</ns2:date>
<ns3:lockStatus xmlns:ns3="http://bean.cmp.app.com">N</ns3:lockStatus>
<ns4:dailyTime xsi:nil="true" xmlns:ns4="http://bean.cmp.app.com"/>
<ns5:taskListNew xsi:nil="true" xmlns:ns5="http://bean.cmp.app.com"/>
</monthListItem>
<monthListItem>
<ns6:date xmlns:ns6="http://bean.cmp.app.com">2-2-2014, Sunday (nonworking day)</ns6:date>
<ns7:lockStatus xmlns:ns7="http://bean.cmp.app.com">N</ns7:lockStatus>
<ns8:dailyTime xmlns:ns8="http://bean.cmp.app.com">04:00</ns8:dailyTime>
<ns9:taskListNew xmlns:ns9="http://bean.cmp.app.com">
<taskListItem>
<ns9:trackId>1070</ns9:trackId>
<ns9:taskId>14</ns9:taskId>
</taskListItem>
<taskListItem>
<ns9:trackId>1094</ns9:trackId>
<ns9:taskId>44</ns9:taskId>
</taskListItem>
</ns9:taskListNew>
</monthListItem>
<monthListItem>
<ns10:date xmlns:ns10="http://bean.cmp.app.com">3-2-2014, Monday</ns10:date>
<ns11:lockStatus xmlns:ns11="http://bean.cmp.app.com">N</ns11:lockStatus>
<ns12:dailyTime xmlns:ns12="http://bean.cmp.app.com">08:00</ns12:dailyTime>
<ns13:taskListNew xmlns:ns13="http://bean.cmp.app.com">
<taskListItem>
<ns13:trackId>1071</ns13:trackId>
<ns13:taskId>14</ns13:taskId>
</taskListItem>
<taskListItem>
<ns13:trackId>1073</ns13:trackId>
<ns13:taskId>44</ns13:taskId>
</taskListItem>
</ns13:taskListNew>
</monthListItem>
</ns1:monthBO>
<ns14:userId xsi:nil="true" xmlns:ns114="http://response.cmp.app.com"/>5</ns14:userId>
</ViewAppTrackResponseReturn>
</ViewAppTrackResponse>
</soapenv:Body>
</soapenv:Envelope>
Can anyone help me to parse this response. This will helpful for me.
LibXML2 is included in Cocoa.
http://www.raywenderlich.com/553/xml-tutorial-for-ios-how-to-choose-the-best-xml-parser-for-your-iphone-project
http://www.cocoawithlove.com/2008/10/using-libxml2-for-parsing-and-xpath.html
You can use NSXMLParser class to parse this.Using its delegate methods you can parse. I am posting my try to parse your xml.It is not completed.I am giving you a basic code for parsing. You have to do the remaining.
Here "xmlInput" is of type NSString with your xmlstring.
NSData* xmlData = [xmlInput dataUsingEncoding:NSUTF8StringEncoding];
NSXMLParser * xmlParser = [[NSXMLParser alloc] initWithData:[xmlData copy]];
[xmlParser setDelegate:(id)self];
[xmlParser setShouldResolveExternalEntities: YES];
[xmlParser parse];
Create an xmlparser object and input your xmlData.Set its delegates.
//this delegate calls when parsing start.Only once.
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
recordResults = NO;//declared in .h
MonthFlag = NO;//declared in .h
TaskFlag = NO;//declared in .h
Arry = nil;//declared in .h
}
// This delgate calls when each tag name is found.
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *) namespaceURI qualifiedName:(NSString *)qName
attributes: (NSDictionary *)attributeDict
{
strElementName = [elementName copy];//strElementName is declared in .h
NSLog(#"%#",strElementName);
if([elementName isEqualToString:#"monthListItem"]){
MonthFlag = YES;
}
if([elementName isEqualToString:#"taskListItem"]){
TaskFlag = YES;
}
strElementValue = #""; //strElementValue is declared in .h
}
//This is called when each tag value is found.
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
strElementValue = [NSString stringWithFormat:#"%#%#", strElementValue,[string copy]];
NSLog(#"%#",strElementValue);
//NSLog(#"%# : %#",strElmentName,strElementValue);
recordResults=(strElementValue.length > 0);
}
// This deleagte will call in the end of each tag name.
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI: (NSString *)namespaceURI qualifiedName:(NSString *)qName
{
NSLog(#"%# - %#",elementName,strElementValue);
if (recordResults) {
if (MonthFlag) {
if(dicTemp==nil){
dicTemp = [[NSMutableDictionary alloc] init];
for (int i=0; i<10; i++) {
[dicTemp setObject:#"" forKey:strElementName];
}
}
[dicTemp setObject:strElementValue forKey:elementName ];
}
}
if(([elementName isEqualToString:#"monthListItem"] ) && dicTemp!=nil) {
if(Arry==nil)Arry = [[NSMutableArray alloc] init];
[Arry addObject:[dicTemp copy]];
dicTemp = nil;
[dicTemp release];
MonthFlag = NO;
NSLog(#"arry test %#",[Arry description]);
}
}
// This delegate will call when parsing finishes . only once
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
recordResults = NO;
}

NSXMLParser is not parsing HTML tags

I am trying to parse XML which is as follows.
<xml>
<item>
<title>
21/2/2014 13:18:22
</title>
<time>
2014-02-21 02:49:03
</time>
<message>
<strong>
abcd</strong><br /><br /><em>abcd</em><br /><br /><u>abcd</u><br /><br /><br />
</message>
</item>
<item>
<title>
21/2/2014 12:9:40
</title>
<time>
2014-02-21 01:57:28
</time>
<message>
100
</message>
</item>
</xml>
I am parsing using the normal parsing procedure
My Parser.h
#import "XMLData.h"
#interface XMLParser : NSObject<NSXMLParserDelegate>
{
NSMutableString *currentNodeContent;
NSMutableArray *datas;
NSXMLParser *parser;
XMLData *recentEnquiry;
}
#property (readonly, retain) NSMutableArray *datas;
-(id) loadXMLByURL:(NSString *)urlString;
My Parser.m
#import "XMLParser.h"
#implementation XMLParser
#synthesize datas;
-(id) loadXMLByURL:(NSString *)urlString
{
datas = [[NSMutableArray alloc] init];
NSURL *url = [NSURL URLWithString:urlString];
NSData *data = [[NSData alloc] initWithContentsOfURL:url];
NSLog(#"Data is %#",data);
parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
[parser parse];
return self;
}
- (void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([elementname isEqualToString:#"item"])
{
recentEnquiry = [XMLData alloc];
}
}
- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementname isEqualToString:#"title"])
{
recentEnquiry.title = currentNodeContent;
}
if ([elementname isEqualToString:#"time"])
{
recentEnquiry.time = currentNodeContent;
}
if ([elementname isEqualToString:#"message"])
{
recentEnquiry.message = currentNodeContent;
[datas addObject:recentEnquiry];
}
}
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
currentNodeContent = (NSMutableString *) [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
#end
However for the first item I just get ">" as the message where I should get the text embedded in html tags. I read about other parsing technique knowing that NSXMParser's drawback however I just want the text. I will convert and split the html by myself.
NB: I am getting the desired texts with html tags in foundCharacters method but they come in a loop.
There are two problems here:
The XML is not well-designed: The contents of your message element should encode the HTML. One approach is to replace <, >, and & with <, > and &:
<xml>
<item>
<title>
21/2/2014 13:18:22
</title>
<time>
2014-02-21 02:49:03
</time>
<message>
<strong>
abcd</strong><br /><br /><em>abcd</em><br /><br /><u>abcd</u><br /><br /><br />
</message>
</item>
<item>
<title>
21/2/2014 12:9:40
</title>
<time>
2014-02-21 01:57:28
</time>
<message>
100
</message>
</item>
</xml>
Or, as described by Daij-Djan, you can use CDATA (opened with <![CDATA[ and terminated with ]]>):
<xml>
<item>
<title>
21/2/2014 13:18:22
</title>
<time>
2014-02-21 02:49:03
</time>
<message>
<![CDATA[
<strong>
abcd</strong><br /><br /><em>abcd</em><br /><br /><u>abcd</u><br /><br /><br />
]]>
</message>
</item>
<item>
<title>
21/2/2014 12:9:40
</title>
<time>
2014-02-21 01:57:28
</time>
<message>
100
</message>
</item>
</xml>
For more information about handling the reserved characters of <, > and & in your XML, see section 2.4 Character Data and Markup, of the XML specification.
Your foundCharacters incorrectly assumes that the contents of a field will always be returned by a single call to that method. That's not a valid assumption. You should always assume it may take multiple calls to foundCharacters to return the whole value. Thus, instantiate the currentNodeElement in didStartElement, append to it in foundCharacters, and save and reset it in didEndElement. Thus you may want something like:
- (void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([elementname isEqualToString:#"item"])
{
recentEnquiry = [[XMLData alloc] init];
}
else if ([elementname isEqualToString:#"title"] || [elementname isEqualToString:#"time"] || [elementname isEqualToString:#"message"])
{
currentNodeContent = [NSMutableString string];
}
}
- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementname isEqualToString:#"title"])
{
recentEnquiry.title = [currentNodeContent stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
else if ([elementname isEqualToString:#"time"])
{
recentEnquiry.time = [currentNodeContent stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
else if ([elementname isEqualToString:#"message"])
{
recentEnquiry.message = [currentNodeContent stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
else if ([elementname isEqualToString:#"item"])
{
[datas addObject:recentEnquiry];
}
currentNodeContent = nil;
}
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
[currentNodeContent appendString:string]; // do not trim whitespace here
}
Clearly, currentNodeContent should be defined as a NSMutableString, not just a NSString.

Parsing xml that contains new line characters within nodes

I am trying to parse the XML from a URL in Xcode. The trouble I am having is with new line characters within the nodes. When I parse the following XML, I am getting the address perfectly fine but not the name or description. Also, if there happens to be a "&" symbol, the parser stops parsing. How do I get around these two obstacles?
Here is an example XML I am trying to parse:
<item>
<Name>
John Smith
</Name>
<Address>555 WHITEHEAD ST, Greenville, NY 55555</Address>
<Description>
Brick house - white trim
</Description>
</item>
<item>
<Name>
Jane Smith
</Name>
<Address>555 WHITEHEAD ST, Greenville, NY 55555</Address>
<Description>
Brick house - white trim & stone drive
</Description>
</item>
Here is the code I am using to parse the XML:
-(id) loadXMLByURL:(NSString *)urlString {
arrests = [[NSMutableArray alloc] init];
NSURL *url = [NSURL URLWithString:urlString];
NSData *data = [[NSData alloc] initWithContentsOfURL:url];
parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
[parser parse];
return self;
}
-(void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
if([elementName isEqualToString:#"item"]) {
currentArrest = [[ArrestObject alloc] init];
}
}
-(void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
currentNodeContent = (NSMutableString *) [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
-(void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#"channel"]) {
return;
}
if ([elementName isEqualToString:#"item"]) {
[self.arrests addObject:currentArrest];
currentArrest = nil;
}
else {
[currentArrest setValue:currentNodeContent forKey:elementName];
currentNodeContent = nil;
}
}
Any advice is greatly appreciated!
foundCharacters: can be called more than once per element. You need to append each string you get to your currentNodeContent variable, not just set it. So basically, reset currentNodeContent in the didStartElement method and use the fully appended value in the didEndElement method.
And you can't use stringByTrimmingCharactersInSet to get rid of the newlines because that only removes the whitespace from the ends of the string, not the middle.
Lastly, the problem with the & character is simple - it's not a valid character in XML. Whenever you need an actual & character in your XML, you need to use &, like in HTML. Same for < and > - use < and > respectively.
Update:
In didStartElement do:
currentNodeContent = [NSMutableString string];
In foundCharacters do:
[currentModeContent appendString:string];

How to implement this method in my NSXMLParser to extract images

i'm new in iOS development, and at this moment i have implemented the NSXMLparser , but i really don't know how to separate tags with the same name, but different content, like the <description>. In some feeds, this tag has only the summary and in other, contains the " img src ", which i want to extract too. (with or without CDATA)
Example of description tags wich i need to grab the images and then pass to my UIImageView:
<description><![CDATA[ <p>Roger Craig Smith and Troy Baker to play Batman and the Joker respectively in upcoming action game; Deathstroke confirmed as playable character. </p><p><img src="http://image.com.com/gamespot/images/2013/139/ArkhamOrigins_29971_thumb.jpg"
<description><img src="http://cdn.gsmarena.com/vv/newsimg/13/05/samsung-galaxy-s4-active-photos/thumb.jpg" width="70" height="92" hspace="3" alt="" border="0" align=left style="background:#333333;padding:0px;margin:0px 4px 0px 0px;border-style:solid;border-color:#aaaaaa;border-width:1px" /> <p>
I think that #Rob example solves my case but i don't know how to include in my NSXMLParser, described below, to separate data and images. I'm able to grab only the data (summary) on this parser.
My NSXMLParser:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
element = [elementName copy];
if ([elementName isEqualToString:#"item"])
{
elements = [[NSMutableDictionary alloc] init];
title = [[NSMutableString alloc] init];
date = [[NSMutableString alloc] init];
summary = [[NSMutableString alloc] init];
link = [[NSMutableString alloc] init];
img = [[NSMutableString alloc] init];
imageLink = [[NSMutableString alloc]init];
}
if([elementName isEqualToString:#"media:thumbnail"]) {
NSLog(#"thumbnails media:thumbnail: %#", attributeDict);
imageLink = [attributeDict objectForKey:#"url"];
}
if([elementName isEqualToString:#"media:content"]) {
NSLog(#"thumbnails media:content: %#", attributeDict);
imageLink = [attributeDict objectForKey:#"url"];
}
if([elementName isEqualToString:#"enclosure"]) {
NSLog(#"thumbnails Enclosure %#", attributeDict);
imageLink = [attributeDict objectForKey:#"url"];
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if ([element isEqualToString:#"title"])
{
[title appendString:string];
}
else if ([element isEqualToString:#"pubDate"])
{
[date appendString:string];
}
else if ([element isEqualToString:#"description"])
{
[summary appendString:string];
}
else if ([element isEqualToString:#"media:description"])
{
[summary appendString:string];
}
else if ([element isEqualToString:#"link"])
{
[link appendString:string];
}
else if ([element isEqualToString:#"url"]) {
[imageLink appendString:string];
}
else if ([element isEqualToString:#"src"]) {
[imageLink appendString:string];
}
else if ([element isEqualToString:#"content:encoded"]){
NSString *imgString = [self getImage:string];
if (imgString != nil) {
[img appendString:imgString];
NSLog(#"Content of img:%#", img);
}
}
-(NSString *) getImage:(NSString *)htmlString {
NSString *url = nil;
NSScanner *theScanner = [NSScanner scannerWithString:htmlString];
[theScanner scanUpToString:#"<img" intoString:nil];
if (![theScanner isAtEnd]) {
[theScanner scanUpToString:#"src" intoString:nil];
NSCharacterSet *charset = [NSCharacterSet characterSetWithCharactersInString:#"\"'"];
[theScanner scanUpToCharactersFromSet:charset intoString:nil];
[theScanner scanCharactersFromSet:charset intoString:nil];
[theScanner scanUpToCharactersFromSet:charset intoString:&url];
}
return url;
}
#end
In your example you just have two description elements, each which has the img tag embedded within it. You just parse the description like normal, and then pull out the img tags (using regular expressions, using my retrieveImageSourceTagsViaRegex below, or a scanner).
Note, you do not have to handle the CDATA and non-CDATA renditions differently if you don't want. While NSXMLParserDelegate provides a foundCDATA routine, I'd actually be inclined to not implement that. In the absence of a foundCDATA, the standard foundCharacters routine of NSXMLParser will gracefully handle both renditions of your description tag (with and without CDATA) seamlessly.
Consider the following hypothetical XML:
<xml>
<descriptions>
<description><![CDATA[ <p>Roger Craig Smith and Troy Baker to play Batman and the Joker respectively in upcoming action game; Deathstroke confirmed as playable character. </p><p><img src="http://image.com.com/gamespot/images/2013/139/ArkhamOrigins_29971_thumb.jpg">]]></description>
<description><img src="http://cdn.gsmarena.com/vv/newsimg/13/05/samsung-galaxy-s4-active-photos/thumb.jpg" width="70" height="92" hspace="3" alt="" border="0" align=left style="background:#333333;padding:0px;margin:0px 4px 0px 0px;border-style:solid;border-color:#aaaaaa;border-width:1px" /> <p></description>
</descriptions>
</xml>
The following parser will parse both of those description entries, grabbing the image URLs out of them. And as you'll see, there is no special handling for CDATA needed:
#interface ViewController () <NSXMLParserDelegate>
#property (nonatomic, strong) NSMutableString *description;
#property (nonatomic, strong) NSMutableArray *results;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSURL *filename = [[NSBundle mainBundle] URLForResource:#"test" withExtension:#"xml"];
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:filename];
parser.delegate = self;
[parser parse];
// full array of dictionary entries
NSLog(#"results = %#", self.results);
}
- (NSMutableArray *)retrieveImageSourceTagsViaRegex:(NSString *)string
{
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"(<img\\s[\\s\\S]*?src\\s*?=\\s*?['\"](.*?)['\"][\\s\\S]*?>)+?"
options:NSRegularExpressionCaseInsensitive
error:&error];
NSMutableArray *results = [NSMutableArray array];
[regex enumerateMatchesInString:string
options:0
range:NSMakeRange(0, [string length])
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
[results addObject:[string substringWithRange:[result rangeAtIndex:2]]];
}];
return results;
}
#pragma mark - NSXMLParserDelegate
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
self.results = [NSMutableArray array];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqualToString:#"description"])
self.description = [NSMutableString string];
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if (self.description)
[self.description appendString:string];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:#"description"])
{
NSArray *imgTags = [self retrieveImageSourceTagsViaRegex:self.description];
NSDictionary *result = #{#"description": self.description, #"imgs" : imgTags};
[self.results addObject:result];
self.description = nil;
}
}
#end
That yields the following results (note, no CDATA):
results = (
{
description = " <p>Roger Craig Smith and Troy Baker to play Batman and the Joker respectively in upcoming action game; Deathstroke confirmed as playable character. </p><p><img src=\"http://image.com.com/gamespot/images/2013/139/ArkhamOrigins_29971_thumb.jpg\">";
imgs = (
"http://image.com.com/gamespot/images/2013/139/ArkhamOrigins_29971_thumb.jpg"
);
},
{
description = "<img src=\"http://cdn.gsmarena.com/vv/newsimg/13/05/samsung-galaxy-s4-active-photos/thumb.jpg\" width=\"70\" height=\"92\" hspace=\"3\" alt=\"\" border=\"0\" align=left style=\"background:#333333;padding:0px;margin:0px 4px 0px 0px;border-style:solid;border-color:#aaaaaa;border-width:1px\" /> <p>";
imgs = (
"http://cdn.gsmarena.com/vv/newsimg/13/05/samsung-galaxy-s4-active-photos/thumb.jpg"
);
}
)
So, bottom line, just parse the XML like normal, don't worry about CDATA, and just parse out the image URL using a NSScanner or NSRegularExpression as you see fit.

Resources