How to implement this method in my NSXMLParser to extract images - ios

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.

Related

iOS NSXMLParser - Consistently Derive Image Source URL From XML Tag

I'm working with RSS feeds in my app, specifically with Drudge Report's. I'm quite new to this sort of stuff, along with being new to using Xcode's NSXMLParser. Each feed apparently represents an article. Each feed is represented by the <item></item> tags.
Within these tags, there's a description of info enclosed by the <description></description> tags. In the description, some articles might have an image associated with that article, as seen in the following screenshot:
The part I highlighted is the image I need to get (specifically, the URL string). I'm able to derive the description each article as an NSMutableString, but how do I derive the image's URL when I parse the XML with NSXMLParser? The following is my code so far as to how I'm getting all of this done:
#interface ViewController () <NSXMLParserDelegate, UITableViewDataSource, UITableViewDelegate> {
NSXMLParser *parser;
NSMutableArray *feeds;
NSMutableDictionary *item;
NSMutableString *title;
NSMutableString *link;
NSMutableString *description;
NSString *element;
}
.
.(other code)
.
#pragma mark - NSXMLParserDelegate
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
element = elementName;
if ([element isEqualToString:#"item"]) {
item = [[NSMutableDictionary alloc] init];
title = [[NSMutableString alloc] init];
link = [[NSMutableString alloc] init];
description = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if ([element isEqualToString:#"title"]) {
[title appendString:string];
}
else if ([element isEqualToString:#"feedburner:origLink"]) {
[link appendString:string];
}
else if ([element isEqualToString:#"description"]) {
[description appendString:string];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#"item"]) {
NSString *filteredTitle = [title stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSString *filteredLink = [link stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if (![filteredLink containsString:#"https://itunes.apple.com/"]) {
[item setObject:filteredTitle forKey:#"title"];
[item setObject:filteredLink forKey:#"link"];
[item setObject:description forKey:#"description"];
[feeds addObject:[item copy]];
}
}
}
- (void)parserDidEndDocument:(NSXMLParser *)parser {
[self.tableView reloadData];
}
PROGRESS
So far, I added the following in my didEndElement method:
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#"item"]) {
NSString *filteredTitle = [title stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSString *filteredLink = [link stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if (![filteredLink containsString:#"https://itunes.apple.com/"]) {
[item setObject:filteredTitle forKey:#"title"];
[item setObject:filteredLink forKey:#"link"];
[item setObject:description forKey:#"description"];
if ([description rangeOfString:#"img style"].location != NSNotFound)
{
}
[feeds addObject:[item copy]];
}
}
}
Now that I know that the description has the img style string in it, I need to get the src="whateverImageURL". How do I use a regular expression to get the first occurrence of this image URL?
You'l have to do the following in ur
foundCharacters: method.
else if ([element isEqualToString:#"description"])
{
[description appendString:string];
if ([description rangeOfString:#"img"].location != NSNotFound)
{
NSRange firstRange = [previewImage rangeOfString:#"src="];
NSRange endRange = [[previewImage substringFromIndex:firstRange.location] rangeOfString:#" width=\""];
NSString *finalLink = [[NSString alloc] init];
finalLink = [previewImage substringWithRange:NSMakeRange(firstRange.location, endRange.location)];
NSString *match = #"src=\"";
NSString *postMatch;
NSScanner *scanner = [NSScanner scannerWithString:finalLink];
[scanner scanString:match intoString:nil];
postMatch = [finalLink substringFromIndex:scanner.scanLocation];
NSString *finalURL = [postMatch stringByAppendingString:#""];
description = finalURL;
}
}
}
Since in ur foundCharacters u are already getting the description tag
u need to search for the text in ur description array where u append
the string.
that u can do by scanning the entire string then store the required
substring in a variable...i.e ur URL link
Use firstRange variable to set the range from where ull take the string
and endrange variable to set the text till where u want the string to end (in ur case the url)
Here i m storing the URL in previewImage.
Hope it works for u good luck.....
After some research, I've managed to solve my problem. I just needed a little practice with using NSRange. The idea is, in my case, that when I have a description that has the NSString "img style" in it, I know for a fact that I need the first "src="whateverImageURL" string that I can get. I do this in the following code:
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:#"item"]) {
NSString *filteredTitle = [title stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSString *filteredLink = [link stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if (![filteredLink containsString:#"https://itunes.apple.com/"]) {
[item setObject:filteredTitle forKey:#"title"];
[item setObject:filteredLink forKey:#"link"];
[item setObject:description forKey:#"description"];
if ([description rangeOfString:#"img style"].location != NSNotFound) {
NSString *finalImageURL;
NSRange startRange = [description rangeOfString:#"src=\""];
finalImageURL = [description substringFromIndex:startRange.location];
finalImageURL = [finalImageURL substringFromIndex:startRange.length];
NSRange endRange = [finalImageURL rangeOfString:#"\""];
finalImageURL = [finalImageURL substringToIndex:endRange.location];
}
[feeds addObject:[item copy]];
}
}
}
you have to implement this protocol
- (void)parser:(NSXMLParser *)parser foundAttributeDeclarationWithName:(NSString *)attributeName forElement:(NSString *)elementName type:(nullable NSString *)type defaultValue:(nullable NSString *)defaultValue;
this allow you to get all attribute for each element found.
Let me know if this help you :)
UPDATE
Here a code that find the url of first img found in a given string
NSString *descriptionString = #"<br><tt><font size=\"3\" color=\"blue\"><b><u>LIST: 10 Worst Winter Storms in Washington History...</u></b></font></tt><br><br><br><font face=\"Arial\" size=\"1\"><i>(Top headline, 3rd story, <a href=\"http://www.nbcwashington.com/news/local/Ten-Worst-Storms-in-DC-History-365815301.html\">link</a>)</i></font><hr style=\"height: 1px; border-style: none; color: #666666; background-color: #666666;\"/><font face=\"Arial\" size=\"2\">Related stories:<div class=\"related-links\" id=\"R:H1:S3\"><a href=\"http://www.wunderground.com/US/DC/001.html#WIN\">BLIZZARD WARNING ISSUED FOR DC; BURBS UP TO 30\"...</a><br><a href=\"http://washington.cbslocal.com/2016/01/19/winter-is-finally-here-deep-freeze-and-snow-in-the-forecast/\">Mayor Requests Help From National Guard...</a><br><a href=\"http://www.accuweather.com/en/weather-news/snow-storm-travel-disruptions-aim-for-nyc-dc-boston-philadelphia-friday-saturday/54870622\">UPDATE...</a><br><a href=\"http://www.infowars.com/snowmaggedon2016-empty-store-shelves-as-panicked-shoppers-ransack-grocery-stores/\">Anxious Shoppers Ransack Grocery Stores...</a><br><a href=\"http://motherboard.vice.com/read/dark-web-users-are-worried-snowstorm-jonas-will-disrupt-their-deliveries\">Dark Web Users Fear Delivery Disruptions...</a><br><a href=\"https://www.washingtonpost.com/news/to-your-health/wp/2016/01/21/heres-why-some-people-drop-dead-while-shoveling-snow/\">Cold weather, shoveling form heart attack 'perfect storm'...</a><br></div></font><br><div class=\"feedflare\"> <a href=\"http://feeds.feedburner.com/~ff/DrudgeReportFeed?a=Mtf4NlmV8XU:vDGXzaysxPw:yIl2AUoC8zA\"><img src=\"http://feeds.feedburner.com/~ff/DrudgeReportFeed?d=yIl2AUoC8zA\" border=\"0\"></img></a> <a href=\"http://feeds.feedburner.com/~ff/DrudgeReportFeed?a=Mtf4NlmV8XU:vDGXzaysxPw:V_sGLiPBpWU\"><img src=\"http://feeds.feedburner.com/~ff/DrudgeReportFeed?i=Mtf4NlmV8XU:vDGXzaysxPw:V_sGLiPBpWU\" border=\"0\"></img></a> <a href=\"http://feeds.feedburner.com/~ff/DrudgeReportFeed?a=Mtf4NlmV8XU:vDGXzaysxPw:qj6IDK7rITs\"><img src=\"http://feeds.feedburner.com/~ff/DrudgeReportFeed?d=qj6IDK7rITs\" border=\"0\"></img></a> <a href=\"http://feeds.feedburner.com/~ff/DrudgeReportFeed?a=Mtf4NlmV8XU:vDGXzaysxPw:gIN9vFwOqvQ\"><img src=\"http://feeds.feedburner.com/~ff/DrudgeReportFeed?i=Mtf4NlmV8XU:vDGXzaysxPw:gIN9vFwOqvQ\" border=\"0\"></img></a> </div><img src=\"http://feeds.feedburner.com/~r/DrudgeReportFeed/~4/Mtf4NlmV8XU\" height=\"1\" width=\"1\" alt=\"\"/&gt";
NSString *stringWithoutWhiteSpace = [descriptionString stringByReplacingOccurrencesOfString:#" " withString:#""];
NSInteger srcLocation = [stringWithoutWhiteSpace rangeOfString:#"src="].location;
if ( srcLocation!= NSNotFound) {
NSString *firstSrcImg = [stringWithoutWhiteSpace substringFromIndex:srcLocation];
NSArray *componment = [firstSrcImg componentsSeparatedByString:#"\""];
NSString *url = componment[1];
NSLog(#"%#", url);
}
i invite you to try it and tell me if it respond to your question ...
i can give another code that return all img urls :)
SECOND UPDATE
For the example i have done here a method that you can use:
- (NSString*) getNextURLFromString:(NSString*) str withURLTag:(NSString*) urlTag{
NSString *stringWithoutWhiteSpace = [str stringByReplacingOccurrencesOfString:#" " withString:#""];
NSInteger srcLocation = [stringWithoutWhiteSpace rangeOfString:urlTag].location;
if ( srcLocation!= NSNotFound) {
NSString *firstSrcImg = [stringWithoutWhiteSpace substringFromIndex:srcLocation];
NSArray *componment = [firstSrcImg componentsSeparatedByString:#"\""];
NSString *url = componment[1];
return url;
}
return nil;
}
for the urlTag param put #"src="
and for the str param put the description tag value
UPDATE N° 3
here a method that return all images url
- (NSArray*) getAllURLFromString:(NSString*) str withURLTag:(NSString*) urlTag{
NSMutableArray *result = [NSMutableArray array];
NSString *stringWithoutWhiteSpace = [str stringByReplacingOccurrencesOfString:#" " withString:#""];
NSInteger srcLocation = [stringWithoutWhiteSpace rangeOfString:urlTag].location;
if ( srcLocation!= NSNotFound) {
NSString *firstSrcImg = [stringWithoutWhiteSpace substringFromIndex:srcLocation];
NSArray *componment = [firstSrcImg componentsSeparatedByString:#"\""];
if ([componment count]>1) {
NSString *url = componment[1];
[result addObject:url];
NSArray *nextComponent = [stringWithoutWhiteSpace componentsSeparatedByString:url];
if ([nextComponent count]>1) {
[result addObjectsFromArray:[self getAllURLFromString:nextComponent[1] withURLTag:urlTag]];
}
}
return result;
}
return result;
}
for the urlTag param put #"src="
and for the str param put the description tag value

XML data parsing in xcode and storing it

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:

string convert in array Ios , get by webservices

In ios i am using Xml soap parsing This is my response
<InsertResponse xmlns="http://OrderMe.solyn.in/"><InsertResult>[{"Result":"Success","AccessToken":"f60da1f1-40d7-483d-880a-82348dc20934","AppUserId":"35"}]</InsertResult></InsertResponse>
Then i am using This code for Get Response
-(void)parser:(NSXMLParser *) parser foundCharacters:(NSString *)string {
if (elementFound) {
// NSLog(#"%#",soapResults);
[soapResults appendString: string];
if ([Type isEqualToString:#"InsertResponse"] ) {
//--- Retrieving text of an element that is found---
NSLog(#"%#",string);
NSString *str = [[NSString alloc]initWithData:string encoding:NSUTF8StringEncoding];
NSArray *allData = [str JSONValue];
//NSString *loginID=[NSString stringWithFormat:#"%#",string];
//NSLog(#"Login ID Returned from web service is : %#",loginID);
}
}
}
in this code ** NSLog(#"%#",string);** this string is print
[{"Result":"Success","AccessToken":"f60da1f1-40d7-483d-880a-82348dc20934","AppUserId":"35"}]
so i dont know how to convert this string in array
I am wating response
please share your valuable knowledge
Regards,
Nishant Chandwani
Thanks .
You need to parse this string as JSON:
NSString *string = #"[{\"Result\":\"Success\",\"AccessToken\":\"f60da1f1-40d7-483d-880a-82348dc20934\",\"AppUserId\":\"35\"}]";
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
First this is xml parser.So how can we get the values from this.Let's come to below coding
In your .h part
//Step 1 : Add the Delegate classes
First of all you should add <NSXMLParserDelegate>
//Step 2 : Create necessary objects
NSXMLParser *parser;
NSMutableData *ReceviedData;
NSMutableString *currentStringValue;
NSMutableArray *arrayResult;
NSMutableArray *arrayAccessToken;
NSMutableArray *arrayAppUserId;
In your .m part
//Step 3 - Allocate your all Arrays in your viewDidLoad method
arrayAppUserId = [NSMutableArray alloc]init];
arrayResult = [NSMutableArray alloc]init];
arrayAccessToken = [NSMutableArray alloc]init];
//Step 4 - Create Connection in your viewDidLoad Like
[self createConnection:#"http://www.google.com"];//give your valid url.
-(void)createConnection:(NSString *)urlString
{
NSURL *url = [NSURL URLWithString:urlString];
//Step 5 - parser delegate methods are using NSURLConnectionDelegate class or not.
BOOL success;
if (!parser)
{
parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
parser.delegate = self;
parser.shouldResolveExternalEntities = YES;
success = [parser parse];
NSLog(#"Success : %c",success);
}
}
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
NSLog(#"Current Element Name : %#",elementName);
if ([elementName isEqualToString:#"Result"])
{
NSLog(#"The Result is==%#",elementName);
}
if ([elementName isEqualToString:#"AccessToken"])
{
NSLog(#"The AccessToken is==%#",elementName);
}
if ([elementName isEqualToString:#"AppUserId"])
{
NSLog(#"The appUserId is==%#",elementName);
}
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
currentStringValue = [[NSMutableString alloc] initWithString:string];
NSLog(#"Current String Value : %#",currentStringValue);
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:#"Result"])
{
[arrayResult addObject:currentStringValue];
}
if ([elementName isEqualToString:#"AccessToken"])
{
[arrayAccessToken addObject:currentStringValue];
}
if ([elementName isEqualToString:#"AppUserId"])
{
[arrayAppUserId addObject:currentStringValue];
}
currentStringValue = nil;
}

NSXMLParser "foundCharacters" delegate method not called

I was having issues parsing XML, so I went through my code with NSLogs and noticed
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
was not called.
My XML:
<?xml version="1.0"?>
<locations>
<location>
<name>Americas</name>
<address></address>
<gid>CoQBdAAAAE2tdzw4RGvnvaUZTk6N1ByNtosmD--ZYfcmkkPkR6R6v_nN9gJCO1INOVg-S5rzy7BATEUmvAQzh8hClafZbph2wSgfD28gXNJAttXLUbqzQMaql3rVbisSUv2a2r_H6ktOSaI5lLvf0GNthT5jn2JfAdD_HfUls6qhMvrm5e5XEhDcF5uRjlfpSqI_aciS_QPaGhQVVHenbOKZBQMSoCzsoIsdfw313Q</gid>
<type>
<1>political</1><2/><3/><4/><5/>
</type>
<phone></phone>
<rating></rating>
<icon>http://maps.gstatic.com/mapfiles/place_api/icons/geocode-71.png</icon>
<website></website>
<longitude>-105.2551187</longitude>
<latitude>54.5259614</latitude>
</location>
</locations>
Relevant Part of my Implementation
- (void)viewDidLoad
{
[super viewDidLoad];
...
if(!self.distance){self.distance = #"250000";}
NSString *string = [NSString stringWithFormat:#"http://fishbe.in/lab/reserverr/api/?term=%#&lat=%f&long=%f&limit=100&distance=%#", self.searchterm, location.coordinate.latitude, location.coordinate.longitude, self.distance];
NSLog(#"%#", string);
NSURL *URL = [NSURL URLWithString:string];
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:URL];
[parser setDelegate:self];
[parser setShouldResolveExternalEntities:NO];
[parser setShouldProcessNamespaces:NO];
[parser setShouldReportNamespacePrefixes:NO];
[parser parse];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
self.element = elementName;
if ([self.element isEqualToString:#"locations"]) {
self.items = [[NSMutableArray alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
NSLog(#"HI"); //Not outputted
if ([self.element isEqualToString:#"name"]) {
self.result = [[ReserverrSearchResult alloc] init];
[self.result setTitle:string];
NSLog(#"%#", string);
} else if ([self.element isEqualToString:#"address"]) {
[self.result setVicinity:string];
NSLog(#"%#", string);
} else if ([self.element isEqualToString:#"gid"]) {
[self.result setGid:string];
NSLog(#"%#", string);
} else if ([self.element isEqualToString:#"latitude"]) {
[self.result setLatitude:[string floatValue]];
NSLog(#"%#", string);
} else if ([self.element isEqualToString:#"longitude"]) {
[self.result setLongitude:[string floatValue]];
NSLog(#"%#", string);
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#"location"]) {
[self.items addObject:self.result];
}
}
- (void)parserDidEndDocument:(NSXMLParser *)parser {
[self addAnnotations];
}
Edit - Added
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
NSLog(#"%#",parseError);
}
and got nothing.
There are a couple of issues:
As wain points out, you should implement parser:parseErrorOccurred: and it will tell you what's wrong.
I'd expect problems if the searchterm contains any characters that are reserved characters for a URL (e.g. a space, a plus sign, an ampersand, etc.). You should always percent-escape what I presume is a user provided search term and adding it to a URL:
NSString *string = [NSString stringWithFormat:#"http://fishbe.in/lab/reserverr/api/?term=%#&lat=%f&long=%f&limit=100&distance=%#", [self percentEscapeString:self.searchterm], location.coordinate.latitude, location.coordinate.longitude, self.distance];
where you can use a method like the following:
- (NSString *)percentEscapeString:(NSString *)string
{
NSString *result = CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
(CFStringRef)string,
(CFStringRef)#" ",
(CFStringRef)#":/?#!$&'()*+,;=",
kCFStringEncodingUTF8));
return [result stringByReplacingOccurrencesOfString:#" " withString:#"+"];
}
Note, do not rely upon stringByAddingPercentEscapesUsingEncoding to do the percent-escaping. You really want to use CFURLCreateStringByAddingPercentEscapes, like above.
As an aside, your XML is not well-formed. I don't believe that NSXMLParser will accept element names that are numbers. So, instead of:
<type>
<1>political</1><2/><3/><4/><5/>
</type>
You might want:
<types>
<type>political</type>
<type></type>
<type></type>
<type></type>
<type></type>
</types>
Or, if you really need those numeric identifiers:
<types>
<type id="1">political</type>
<type id="2"></type>
<type id="3"></type>
<type id="4"></type>
<type id="5"></type>
</types>
In this latter example, your didStartElement can then use the attributeDict to extract the id identifier.
You report that parseErrorOccurred did not return anything. That means your server probably didn't return anything. And this could be caused by any of a number of problems. Try using your URL and return the data (for example, into a NSString or NSData, rather than the parser) and see what the response looked like. E.g., for testing purposes you could do:
NSError *error;
NSData *data = [NSData dataWithContentsOfURL:url options:0 error:&error];
NSString *results = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
// confirm error is `nil` and `results` is well-formed XML;
// you can then pass the `data` to `NSXMLParser` with `initWithData` if you want
I bet it's empty (which for certain search terms could be caused by a lack of percent-escaping, or from any of a myriad of server problems).
It might not be a problem, but it's worth noting that it's conceptually risky to save the results in foundCharacters, because sometimes between didStartElement and didEndElement, you'll have multiple calls to foundCharacters to retrieve the data. Generally people will instantiate a NSMutableString in didStartElement, append to it in foundCharacters, and then save it in didEndElement.

NSXMLParser strange behavior with \n

I'm developing an iOS 4 application that parses a XML file. A piece of XML is:
<?xml version="1.0" encoding="utf-8" ?>
<cards>
<card id ="0">
<name lang="es">The Mad</name>
<description lang="es">...</description>
</card>
...
</cards>
I use the following method to parse <name lang="es">The Mad</name>.
#pragma mark -
#pragma mark NSXMLParserDelegate methods
- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict
{
NSLog(#"DidStartElement: %#", elementName);
if ([elementName isEqualToString:#"card"])
{
currentCard = [[Card alloc] init];
NSString* arcaneNumber = [attributeDict objectForKey:#"id"];
currentCard.Number = arcaneNumber;
return;
}
if ([elementName isEqualToString:#"name"])
{
if ([[attributeDict objectForKey:#"lang"] isEqualToString:userLanguage])
{
currentProperty = kNameProperty;
return;
}
}
if ([elementName isEqualToString:#"description"])
{
if ([[attributeDict objectForKey:#"lang"] isEqualToString:userLanguage])
{
currentProperty = kDescriptionProperty;
return;
}
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if (!currentStringValue)
{
// currentStringValue is an NSMutableString instance variable
currentStringValue = [[NSMutableString alloc] initWithCapacity:50];
}
[currentStringValue appendString:string];
}
- (void) parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
{
NSLog(#"DidEndElement: %#", elementName);
if ([elementName isEqualToString:#"card"])
{
[cards setObject:currentCard forKey:currentCard.Number];
[currentCard release];
currentCard = nil;
return;
}
if (currentProperty == kNameProperty)
{
currentProperty = kNoneProperty;
currentCard.Name = [currentStringValue stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
[currentStringValue release];
currentStringValue = nil;
return;
}
if (currentProperty == kDescriptionProperty)
{
currentProperty = kNoneProperty;
currentCard.Description = currentStringValue;
[currentStringValue release];
currentStringValue = nil;
return;
}
}
After, parsing , I get on currentStringValue the following:
\n\nThe Mad
How can avoid these two '\n'? Why am I getting these two '\n'?
This xml file came from a Windows system, and I've used TextWrangler to covert it to Classic Mac format.
The foundCharacters delegate method also gets called for whitespace in between elements. It will get called if there is say a newline between <cards> and <card id=....
I suggest clearing currentStringValue at the top of didStartElement to discard any characters found before the start of the current element and to make sure only the characters inside the current element (not between) are captured by foundCharacters.
At the top of didStartElement, add:
[currentStringValue release];
currentStringValue = nil;
It is possible for an element's value to contain whitespace (so removing them in didEndElement could result in saving values that don't match the xml content).
On your end element method you can try stripping these out yourself from the current text string. Simply try searching for them on the string and replace them with #"". I like replacing these and unwanted blank entries.
Try this it work for me ,
NSString *data= [str stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
Use above line of code in foundCharacters method .
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string;
{
NSString *data= [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if(dString == nil){
dString = [[NSMutableString alloc] initWithString:data];
}else
{
[dString appendString:data];
}
}
Hope this will help for some one .

Resources