Hey I have used parsers in the past but never in an ARC environment. Now when I try and set my parser to self it gives me an error message and then doesn't parse my data. Anyone know what the problem is and how to solve it?
Here is the warning I get when I try to set the parser delegate to self:
Semantic Issue: Sending 'CharityController *const __strong' to parameter of incompatible type 'id<NSXMLParserDelegate>'
Here is my code:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
if (connection == theConnection)
{
// do something with the data object.
//NSLog(#"Data from server is: %#",data);
NSString *test = [[NSString alloc]initWithData:data encoding:NSStringEncodingConversionAllowLossy];
NSLog(#"Data from server is: %#",test);
parser = [[NSXMLParser alloc] initWithData: data];
[parser setDelegate:self];
[parser parse];
}
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
if ([elementName isEqualToString:#"root"]) {
currentStringValue = nil;
NSLog(#"I am inside the Parser at root!.");
return;
}
Did you use the protocol name in the interface definition to let the compiler know you conform to that particular protocol? Such as:
#interface CharityClass : NSObject <NSXMLParserDelegate>
Related
This is my method body for parsing "img src" image links from poorly formed html generated by an RSS feed... I am aware that NSXML only parses XML, but I have this hope that it can stumble through the mess to find these miniscule image links from messy html.
I'm trying to retrieve ONLY the FIRST image link found in the src attribute I find in each element name called IMG in nsData that has a src attribute and then save it to a NSString *img in another class. The img tags are not all the same, for instance an instance of nsData will contain only one image instance like any one of these:
< img class="ms-rteStyle-photoCredit" src="www.imagelinkthatineed.com" stuff I don't need
< img alt="" src="www.imagelinkineedfortableimagecellpreview" stuff I don't need
< img class="ms-rteStyle-photoCredit" src="www.IneedThisLink.com" more stuff I don't need
The only class that seems to generate NSLog output is the first one.
How can I get the parser methods to actually run ?
Given that there's a way, is there a different, simpler way you recommend?
#import "HtmlParser.h"
#import "ArticleItem.h"
#implementation HtmlParser
#synthesize elementArray;
- (HtmlParser *) InitHtmlByString:(NSString *)string {
// NSString *description = [NSString string];
NSData *nsData = [[NSData alloc] initWithContentsOfFile:(NSString *)string];
elementArray = [[NSMutableArray alloc] init];
parser = [[NSXMLParser alloc] initWithData:nsData];
parser.delegate = self;
[parser parse];
If I NSLog(#"%#", nsData); in this method body, the output spits out the raw HTML.
currentHTMLElement = [ArticleItem alloc];
return self;
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqualToString:#"img src"]) {
currentHTMLElement = [[ArticleItem alloc] init];
}
NSLog(#"\t%# found a %# element", self, elementName);
}
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if (!currentHTMLElement)
currentHTMLElement = [[NSMutableString alloc] initWithString:string];
NSLog(#"Processing Value: %#", currentHTMLElement);
}
- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:#"img src"])
{
currentHTMLElement.img = elementName;
[elementArray addObject:currentHTMLElement];
currentHTMLElement = nil;
currentNodeContent = nil;
}
else
{
if (currentHTMLElement !=nil && elementName != nil && ([elementName isEqualToString:#"img src"]))
{
[currentHTMLElement setValue:currentHTMLElement forKey:elementName];
}
}
currentHTMLElement = nil;
}
#end
Thank you for your thoughts.
Given that HTML is generally not well-formed XML, NSXMLParser might not work. If you want to parse HTML, you might refer to this Ray Wenderlich article, How to Parse HTML on iOS. If you've followed those instructions and have added Hpple to your project, you can then retrieve the image src attributes like so:
#import "TFHpple.h"
- (void)retrieveImageSourceTagsViaHpple:(NSURL *)url
{
NSData *data = [NSData dataWithContentsOfURL:url];
TFHpple *parser = [TFHpple hppleWithHTMLData:data];
NSString *xpathQueryString = #"//img";
NSArray *nodes = [parser searchWithXPathQuery:xpathQueryString];
for (TFHppleElement *element in nodes)
{
NSString *src = [element objectForKey:#"src"];
NSLog(#"img src: %#", src);
}
}
Alternatively, and I say this bracing myself for the onslaught of anti-NSRegularExpression responses (in the vein of my all-time favorite Stack Overflow answer), if you want a list of img tags in an html file, you can use the following somewhat complicated regular expression:
- (void)retrieveImageSourceTagsViaRegex:(NSURL *)url
{
NSString *string = [NSString stringWithContentsOfURL:url
encoding:NSUTF8StringEncoding
error:nil];
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"(<img\\s[\\s\\S]*?src\\s*?=\\s*?['\"](.*?)['\"][\\s\\S]*?>)+?"
options:NSRegularExpressionCaseInsensitive
error:&error];
[regex enumerateMatchesInString:string
options:0
range:NSMakeRange(0, [string length])
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
NSString *src = [string substringWithRange:[result rangeAtIndex:2]];
NSLog(#"img src: %#", src);
}];
}
If you wanted to use NSXMLParser, it would look like so:
- (void)retrieveImageSourceTagsViaNSXMLParser:(NSURL *)url
{
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
parser.delegate = self;
[parser parse];
}
#pragma mark - NSXMLParserDelegate methods
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqualToString:#"img"])
{
NSString *src = attributeDict[#"src"];
NSLog(#"img src: %#", src);
}
}
The problem is, in my experience, NSXMLParser is less successful in parsing HTML than LibXML2/Hpple is. I find that on some simple pages, the above works great. But in other situations, it doesn't. Bottom line, While NSXMLParser is great at parsing well-formed XML, I'd be wary of using it for the parsing of HTML.
I have ran into a peculiar problem with NSXMLParser.
For some reason it cuts out all the characters in front of all the norwegian characters æ, ø and å.
However, the problem seems to be the same with all non a-z characters.(All foreign characters)
Examples:
Reality: Mål
Output: ål
Reality: Le chant des sirènes
Output: ènes
Heres an example from the log where I have printed out the string from:
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
Log:
2012-02-22 14:00:01.647 VotePlayClient[2776:207] found characters: Le chant des sir
2012-02-22 14:00:01.647 VotePlayClient[2776:207] found characters: ènes
You can clearly see that it jumps to a new line whenever it encounters a foreign letter.
I believe that I have to figure out how to append the string or something to that effect.
Here are the NSXMLParser files:
SearchXMLParser.h
#import <Foundation/Foundation.h>
#import "Search.h"
#interface SearchXMLParser : NSObject <NSXMLParserDelegate>
{
NSMutableString *currentNodeContent;
NSMutableArray *searchhits;
NSMutableArray *trackhits;
NSXMLParser *parser;
Search *currentSearch;
}
#property (readonly, retain) NSMutableArray *searchhits;
#property (readonly, retain) NSMutableArray *trackhits;
-(id) loadXMLByURL:(NSString *)urlString;
#end
SearchXMLParser.m
#import "SearchXMLParser.h"
#import "Search.h"
#implementation SearchXMLParser
#synthesize searchhits, trackhits;
-(id) loadXMLByURL:(NSString *)urlString
{
searchhits = [[NSMutableArray alloc] init];
trackhits = [[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 *)qName attributes:(NSDictionary *)attributeDict
{
if ([elementname isEqualToString:#"track"])
{
currentSearch = [Search alloc];
}
if ([elementname isEqualToString:#"track"])
{
currentSearch.trackurl = [attributeDict objectForKey:#"href"];
}
}
- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementname isEqualToString:#"name"])
{
[trackhits addObject:currentNodeContent];
}
if ([elementname isEqualToString:#"track"])
{
currentSearch.track = [trackhits objectAtIndex:0];
currentSearch.artist = [trackhits objectAtIndex:1];
currentSearch.album = [trackhits objectAtIndex:2];
[trackhits removeAllObjects];
[searchhits addObject:currentSearch];
[currentSearch release];
currentSearch = nil;
[currentNodeContent release];
currentNodeContent = nil;
}
}
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
NSLog(#"found characters: %#", string);
currentNodeContent = (NSMutableString *) [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
- (void) dealloc
{
[parser release];
[super dealloc];
}
#end
I have already checked SO for answers and found a couple of similar posts, but nothing that gave a clear solution to this problem.
Can anyone shed some light on this problem? :) Any help is much appreciated!
your parser:foundCharacters: method does not work as it should.
This is from the NSXMLParserDelegate Protocol Reference
The parser object may send the delegate several parser:foundCharacters: messages to report the characters of an element. Because string may be only part of the total character content for the current element, you should append it to the current accumulation of characters until the element changes.
you could try something like this (ARC):
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
NSLog(#"found characters: %#", string);
if (!currentNodeContent) {
currentNodeContent = [[NSMutableString alloc] init];
}
[currentNodeContent appendString:string];
}
- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
// your code here
// when you are done with the string:
currentNodeContent = nil;
}
I simply cannot get NSXMLParser to recognize &
here is my XML:
<?xml version="1.1" encoding="UTF-8"?>
<root>
<myURL>http://www.mywebsite.com/info?id=32&page=5</myURL>>
</root>
Here is my parsing code:
-(void)getXml {
NSURL *xmlurl = [[NSURL alloc] initWithString:#"http://www.mywebsite.com/myxml.xml"];
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlurl];
[xmlParser setDelegate:self];
[xmlParser parse];
[xmlParser release];
[dataurl release];
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
NSLog(#"Parser Error: %#", parseError);
}
- (void)parser:(NSXMLParser *)parser validationErrorOccurred:(NSError *)validError {
NSLog(#"Parser validation Error: %#", validError);
}
- (void)parserDidStartDocument:(NSXMLParser *)parser {
NSLog(#"Parser started");
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
NSLog(#"Found key: %#", elementName);
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
NSLog(#"Found Element: %#", string);
}
- (void)parserDidEndDocument:(NSXMLParser *)parser {
NSLog("done"):
}
Here is my output:
Found key: root
Found key: myURL
Found element: http://www.mywebsite.com/info?id=32
Found element: page=5
Found element:
Found key: myURL
The parser is not recognizing the & correctly and is splitting up my url.
I have seen many Stack questions on this issue but none of them have helped and I have read the Apple docs on NSXMLParser as well. Am I missing something here?
I am building with iOS 5.0 to an iPhone 4
Thank you, this is a great insight into the parser process but there were problems using it, maybe due to the features of Xcode 4.5.1
It turned out that assigning a NSString like #"" to a NSMutableString makes it immutable. Instead to set the starting value for the tempString I've had to use
self.tempString = [[NSMutableString alloc] initWithString: #""];
and the parser:foundCharacters method becomes
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
[self.tempString appendString:string];
currentNodeContent = (NSMutableString*) [self.tempString stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
But that's one problem solved. Thanks.
According to the NSXMLParserDelegate documentation:
The parser object may send the delegate several
parser:foundCharacters: messages to report the characters of an
element. Because string may be only part of the total character
content for the current element, you should append it to the current
accumulation of characters until the element changes.
So the usual pattern of usage is to keep appending to a temporary string until the end of the element is reached:
.h
#property (nonatomic,retain) NSMutableString *tempString;
.m
#synthesize tempString;
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
self.tempString = #"";
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
[self.tempString appendString:string];
}
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
// do something with self.tempString
}
I previously asked this question XMLParser Advice.
However I am still unable to get it to function properly....
So I guess I will start from scratch:
Located at a certain URL is an XML Tree that looks like this
<result>
//stuff that I dont need
<title>
//thing that I do need
</title>
//stuff that I dont need
<body>
//thing that I do need
</body>
<result>
How the heck do I go about parsing that?
The (useless) code I have so far can be found in the link at the top of this question.
Thank you for your time.
Write a simple class, which will be the parser's delegate.
#interface YourObject : NSObject <NSXMLParserDelegate> {
NSString *title, *body; // object attributes
NSXMLParser *parser; // will parse XML
NSMutableString *strData; // will contains string data being parsed
}
#property(readwrite, copy) NSString *title, body;
// will be used to set your object attributes
-(void)fetchValuesAtURL:(NSString *)url;
#end
The fetchValuesAtURL: method will initiate the parse operation.
#implementation YourObject
#synthesize title, body;
-(id)init {
self = [super init];
if(self) {
title = #"";
body = #"";
parser = nil;
strData = [[NSMutableString alloc] initWithCapacity:10];
}
return self;
}
-(void)fetchValuesAtURL:(NSString *)url {
if(parser) {
[parser release];
}
NSURL *xmlURL = [NSURL URLWithString:url];
parser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];
[parser setDelegate:self];
[parser parse];
}
-(void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict {
// element is about to be parsed, clean the mutable string
[strData setString:#""];
}
// the probably missing method
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
// content (or part of) has been found, append that to the current string
[strData appendString:string];
}
-(void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName {
// element has been parsed, test the element name
// and store strData accordingly
if([elementName isEqualToString:#"title"]) {
self.title = strData;
}
else { // or else if, here you got two elements to parse
self.body = strData;
}
}
-(void)dealloc {
[title release];
[body release];
[strData release];
if(parser) {
[parser release];
}
[super dealloc];
}
#end
Then :
YourObject *obj = [[YourObject alloc] init];
[obj fetchValuesAtURL:#"http://www.site.com/xml/url"];
NSXMLParser's delegate is able to do many more things, as described in Event-Driven XML Programming Guide from Apple.
For complete reference on delegate methods, see NSXMLParserDelegate Protocol Reference.
i am a new developer of iPhone.I have to get the names from this URL
Any API URL
in the table view.. how can i do this please help me..
i am try this by making connection but it is showing HTTP time out error .what is the reason by that and is there is any other way to get the data..
here is my connection code...
static NSString *feedURLString =
> #"http://www.XXXXX.com/XXXXXX/api/XXXXX.php?method=All";
> NSURLRequest *studentURLRequest =[NSURLRequest requestWithURL:[NSURL URLWithString:feedURLString]];
>self.studentFeedConnection =[[[NSURLConnection alloc] initWithRequest:studentURLRequest
> delegate:self] autorelease];
NSAssert(self.studentFeedConnection != nil, #"Failure to create URL connection.");
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
> [[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(addstudent:) name:kAddstudentNotifm object:nil];
>[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(studentError:) name:kstudentErrorNotif object:nil];
> - (void)connection:(NSURLConnection *)connection
> didReceiveResponse:(NSURLResponse *)response { NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
> if ((([httpResponse statusCode]/100) == 2) && [[response MIMEType]
> isEqual:#"application/atom+xml"]) {
> self.studentData = [NSMutableData data];
> }
else {
> NSDictionary *userInfo = [NSDictionary dictionaryWithObject:NSLocalizedString(#"HTTP Error", #"Error message
> displayed when receving a connection error.")
>
> forKey:NSLocalizedDescriptionKey];
NSError *error = [NSError errorWithDomain:#"HTTP" code:[httpResponse statusCode]userInfo:userInfo];
> [self handleError:error];
>}
}
>
> - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
> [studentData appendData:data];
}
>
> - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
> [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
> if ([error code] == kCFURLErrorNotConnectedToInternet) {
> // if we can identify the error, we can present a more precise message to the user.
> NSDictionary *userInfo = [NSDictionary dictionaryWithObject: NSLocalizedString(#"No Connection Error", #"Error message displayed when not connected to the Internet.")forKey:NSLocalizedDescriptionKey];
>NSError *noConnectionError = [NSError errorWithDomain:NSCocoaErrorDomain code:kCFURLErrorNotConnectedToInternet userInfo:userInfo];
> [self handleError:noConnectionError];
>}
else {
>// otherwise handle the error generically
>[self handleError:error];
>}
> self.studentFeedConnection = nil;
}
Try using NSXMLParser
NSString *site = [NSString stringWithString:
#"http://www.XXXXX.com/XXX/api/XXXX.php?method=All"];
NSURL *url = [NSURL URLWithString:site];
NSXMLParser *myparser = [NSXMLParser initWithContentsOfURL:url];
myparser.delegate = self;
[myparser parse];
Make sure to implement
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict;
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string;
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError;
- (void) parserDidEndDocument:(NSXMLParser *)parser;
Once you have the data parsed into an NSArray or NSDictionary you can use it as a data source in the UITableView
If you are getting an HTTP error, than chances are there is something wrong with the way in which you connect. Does your connection require authentication? If so you will need to implement:
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
NSURLCredential *myCreds = [[NSURLCredential alloc] initWithUser:#"**USERNAME**" password:#"**PASSWORD**" persistence:NO];
[challenge.sender useCredential:myCreds forAuthenticationChallenge:challenge];
[myCreds release];
}
As for actually retrieving the data like you will need to use NSXML Parser, you can see the details for it here: http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSXMLParser_Class/Reference/Reference.html. There are only 3 methods that you MUST implement, the remaining are just bonus. The three are
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict;
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string;
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName;
Ideally you know the structure of the XML that you are parsing as you will be required to identify the element/attribute name in the above functions. You can then store you data in an array, or whatever fits best.
You also have the option to use a 3rd party parser, there is a very good explanation/comparison of the various available parsers here: http://www.raywenderlich.com/553/how-to-chose-the-best-xml-parser-for-your-iphone-project
Hope this helps!