Multiple NSXMLParser calls - objective-c

I use an API call which returns an XML file. I need to use the same multiple times.
For e.g. on Click of Search button, call http://xyz.com/s1/?para1=srch
Then in a different view, call http://xyz.com/s2/?para2=set2
How should I implement the same? I mean should the XMLParser file be common for both the requests and just the if..else element names should be mixed in a single implementation of parser:didEndElement?
Please help me with an example.

Sure, you can re-use a parser if the page elements are the same. Just make a method in your parser's class that you can feed a location or xml file, and have it parse that file. Something like:
-(void)parseForecast:(NSData *)data; {
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
[parser setDelegate:self];
[parser parse];
[parser release];
}
should do the trick.

Related

Calling multiple XML files

In my application(iPad application) I have 5 folders and inside of each folder i have one XML file. My question is, How can I call all .xml files, in my appDelegate
I have file1.xml, file2.xml, file3.xml, file4.xml,file5.xml (it's a requirement)
for call or adding Path for file1 xml I have this code:
NSString *xml = [[NSBundle mainBundle] pathForResource:#"file1" ofType:#"xml"];
NSData *Data = [NSData dataWithContentsOfFile:xml];
NSXMLParser *Parser = [[NSXMLParser alloc] initWithData:Data];
file1 *parser = [[file1 alloc] initXMLParser];
but how can I have all 5 in my appDelegate class?
and do I need to create specific parser class for each or since all information and tags are the same I just need to add all in my appDelegate
EDIT:
I have to call them from their folder I cann"t change the structure for example
Folder1/file1.xml
Folder2/file2.xml
Folder3/file3.xml and so on
Yes, you need to instantiate an NSXMLParser object for each XML file you're parsing. The simple way to load the XML files is as kimsnarf says: use a for loop and load them in order. If they're in the bundle (which they appear to be), I'd stick them in a specific path under "Resources," like "Resources/SpecialXMLJunk" and just load them by iterating over the results of something like URLsForResourcesWithExtension:subdirectory: (used to get the XML files out of "Resources/SpecialXMLJunk"). So, pseudocode-ish, probably something like this:
NSArray *xmlResourceURLs = [mainBundle
URLsForResourcesWithExtension: #"xml"
subdirectory: xmlResourcesPath];
foreach (NSURL *xmlURL in xmlResourceURLs)
[self loadJunkXMLAtURL: xmlURL];
Create a for-loop and load/parse the files one by one. You should store the parsed data somewhere anyway (in a cache or database) so you don't need to hold on to the files and parsers after parsing. Retrieve data from the cache/database instead.

how to use my imported xml in my objective-c project?

i have this demo xml file:
<trains>
<israel>
<Lehavim>
<lat>31.370201</lat>
<lon>34.798336</lon>
</Lehavim>
<tel-aviv>
<lat>32.073847</lat>
<lon>34.793358</lon>
</tel-aviv>
</israel>
</trains>
loaded to my project using this code:
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL *url = [[NSURL alloc] initWithString:#"http://www.amotech.co/trains.xml"];
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
[xmlParser setDelegate:self];
BOOL success = [xmlParser parse];
if(success)
NSLog(#"success");
else
NSLog(#"Error");
}
i need to get my xml into a UITableView - i want to only to show the name of the train station (for example: "Lehavim" in the TableView.
and after i chose one of my table view items - i want to load the current lat and lon data into double objects.
i would like you to help me with this please.
thanks, amir.
Sounds like you need to get the fundamentals first:
This link takes you to the gory details of NSXMLParser directly from apple.
This link shows a fantastic example of how to use the NSXMLParser.
This link offers some simple examples for adding objects to an array.
This link is a very nice tutorial on UITableViewController.
You will need to provide a little elbow grease.
Quite Tip
To save yourself some headaches and to add flexibility to your xml, I recommend you modify your xml structure such that you aren't using your element names as values.
Change:
<trains>
<israel>
<Lehavim>
<lat>31.370201</lat>
<lon>34.798336</lon>
</Lehavim>
<tel-aviv>
<lat>32.073847</lat>
<lon>34.793358</lon>
</tel-aviv>
</israel>
</trains>
to:
<trains>
<train>
<name>israel</name>
<location>
<name>Lehavim</name>
<lat>31.370201</lat>
<lon>34.798336</lon>
</location>
<location>
<name>tel-aviv</name>
<lat>32.073847</lat>
<lon>34.793358</lon>
</location>
</train>
</trains>

How to handle the exceptions

NSString *stringURL=[[NSString alloc] init];
stringURL=[stringURL stringByAppendingFormat:kSearchBarURL,text.text];
NSString *url = [NSString stringWithString:stringURL];
NSData *data1 = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
NSString *responseids = [[NSString alloc] initWithData:data1 encoding:nil];
responseids = [responseids stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#"\n\r\t"]];
SBJsonParser *parser=[SBJsonParser new];
NSData *data = [parser objectWithString:responseids error:nil];
NSMutableDictionary *searchURL = (NSMutableDictionary*)data;
I coded this i did not handle the exception for my code.
doing json and calling the service url and loading the data.
the application get crashes when my service is too low or no service found.
How to handle the exception for my code here..
Do I use #try #catch.
or
NSURLConnection for error handling.
Please help me out .
Thanks in advance.
Whenever an API makes use of NSError, you should use this rather than wrapping things up in a try…catch block as NSError is designed exactly for this. I usually reserve #try for things where I am really not able to anticipate what might go wrong. If NSError is in the mix, then you know that there is a potential for a problem that you should be handling gracefully.
More generally, your code has some strange stuff in it. You alloc init an empty NSString and then create a new string by appending a format. Not sure why you don't just use [NSString stringWithFormat]. Once you have the string, you can create the URL without the NSString *url bit.
You're also using a synchronous call to what I assume is a remote server. This has the potential to bog down your application if/when the server is not available. You're also not telling NSString what kind of encoding you expect your string to be in when it reads it from NSData. A better method depending on your server side would be to use NSString's stringWithContentsOfURL:usedEncoding:error: method. I would recommend that you use the various NSURLConnection callbacks. Have a look at the URL Loading System Programming Guide on Using NSURLConnection The NSURLConnection delegate methods are the ones you want to implement to provide this asynchronous processing.
For your trimming, you might be interested in the +whitespaceAndNewlineCharacterSet method on NSCharacterSet.
Finally, for your JSON parsing, you might be interested in the category that the SBJSON code adds to NSString, particularly -JSONValue which will give you the dictionary or array representation (as appropriate) of the NSString when parsed as JSON by SBJSON.
HTH

My timer isn't stopping my parsing

I want to put in a timeout in case it takes too long to find my location, send out the relevant url, and parse the xml. It worked when I used performSelector:withObject:afterDelay in the locationManager (just to test getting the xml), but when I put similar code around my parser it doesn't actually abort the parsing. I am testing this by dropping the delay to 0.01.
My problem is: even with the delay set to 0.01, it still waits for all the parsing to complete first, and only then does it put up the alertView that is coded in the parsingDidTimeout method.
I did try this with a timer, and that wasn't working as well as performSelector: does in the other parts of my code. Either way, it doesn't put up the alertView, and stop the parsing, until after the parsing has finished, no matter how long that takes.
I create a url which requires a radius. First I try a small radius, but if I don't get the data I need, I expand the radius and send the url again and parse again. Here is part of my StartParsing method.
xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
XMLParser *parser = [[XMLParser alloc] initXMLParser];
[xmlParser setDelegate:parser];
if (!hadToExpandRadius){//meaning, only do this the first time I send out the url and parse
[self performSelector:#selector(parsingDidTimeout:) withObject:nil afterDelay:0.01];
}
//Start parsing the XML file.
BOOL success = [xmlParser parse];
if(success){
if((didNotGetTheDataYet) && (radius < 500)){
hadToExpandRadius = YES;
radius = radius + 35;
[self startParsing];//do this same method, with larger radius
}
else {
NSLog(#"No Errors");
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(parsingDidTimeout:) object:nil];}
[parser release];
}
-(void)parsingDidTimeout{
[xmlParser abortParsing];
UIAlertView *servicesDisabledAlert = [[UIAlertView alloc] initWithTitle:#"Try Later" message:#"We need a better connection. We can get the data later." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[servicesDisabledAlert show];
[servicesDisabledAlert release];
[myActivityView stopAnimating];
}
Thank you for your help.
Calling performSelector:withObject:afterDelay: you ask the run loop to call the selector later. But [xmlParser parse] blocks the run loop, so it doesn't have a chance to call you selector.
abortParsing is designed to be called inside parsers' delegate methods.
The workaround can be to parse in a separate thread.
Found it -- just extra ":" in my performSelector:#selector(parsingDidTimeout:)!
I thought it was something fancy having to do with the second thread. Just syntax.
Thanks for explaining about the parse blocking the run loop. I was hoping not to need another thread, but your suggestion fixed my problem. Thanks.

How to get a DTD's public and system IDs with NSXMLParser

I'm trying to retrieve the public and system IDs for a DTD in an XML document via NSXMLParser. While NSXMLParser in principal offers publicID and systemID selectors they don't seem to work for me. The doctype tag looks like this:
<!DOCTYPE Article PUBLIC "-//SoftQuad Software//DTD Journalist v2.0 20000501//EN" "file:///C:/Program%20Files/Corel/XMetaL%204/Author/Rules/journalist.dtd">
Here's my code (the file was opened via NSFileHandle's readDataToEndOfFile:
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
[parser setDelegate:self];
BOOL parseSuccessful = [parser parse];
In the delegate's parserDidStartDocument: I try to access the IDs:
NSLog(#"%# : %#", [parser publicID], [parser systemID]);
But I only see
(null) : (null)
From the documentation:
You may invoke this method once a parsing operation has begun or after an error occurs.
So I'd think this should work already in parserDidStartDocument: but I tried to call these selectors in different delegate methods (like parser:didStartElement:namespaceURI:qualifiedName:attributes: but without success.
Any ideas what I'm doing wrong?
You might try out another parser.
http://www.robbiehanson.com/expat.html provides a drop-in replacement for NSXMLParser based on expat.
You might also find this article interesting, comparing the performance of various XML parsers on the iPhone.
http://www.raywenderlich.com/553/how-to-chose-the-best-xml-parser-for-your-iphone-project