How to improve performance with NSXMLParser - objective-c

i'm working with big xml file and need to download and parse him . inside 65k objects, but parsing is more then minute. I cannot understand how to optimize loading/parsing, please help me with advice. Also, because of long work cycle, need to big amount of memory and i don't know how to reduce memory consumption.
AFXMLRequestOperation *operation = [AFXMLRequestOperation
XMLParserRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, NSXMLParser *XMLParser) {
XMLParser.delegate = delegate;
[XMLParser parse];
if (delegate.done) {
NSLog(#"done");
}
} failure:nil];
[operation start];
- (void)parserDidStartDocument:(NSXMLParser *)parser {
_done = NO;
_items = [NSMutableArray new];
_isItem = NO;
_isPrice = NO;
_isDetail = NO;
}
// parsing Ended
- (void)parserDidEndDocument:(NSXMLParser *)parser {
_done = YES;
}
// if parsing error
-(void) parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
_done = YES;
_error = parseError;
}
// if validation error
-(void) parser:(NSXMLParser *)parser validationErrorOccurred:(NSError *)validationError {
_done = YES;
_error = validationError;
}
// new element to parse
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
if ([elementName isEqualToString:kItem]) {
// если да - создаем строку в которую запишем его значение
VZMenuItem *item = [[VZMenuItem alloc] init];
_item = item;
_item.name = attributeDict[#"name"];
_item.code = attributeDict[#"code"];
_isItem = YES;
return;
} else if ([elementName isEqualToString:kAttributes]) {
VZRecipe *recipe = [[VZRecipe alloc] init];
recipe.weight = [attributeDict[#"weight"] floatValue];
recipe.sugar = [attributeDict[#"sugar"] floatValue];
recipe.calories = [attributeDict[#"calories"] intValue];
recipe.milk = [attributeDict[#"milk"] floatValue];
recipe.eggs = [attributeDict[#"eggs"] unsignedIntValue];
_item.recipe = recipe;
return;
} else if ([elementName isEqualToString:kPrice]) {
_isPrice = YES;
return;
} else if ([elementName isEqualToString:kDetail]) {
_isDetail = YES;
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:kItem]) {
[_items addObject:_item];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if(_isPrice) {
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
_item.price = [formatter numberFromString:string];
_isPrice = NO;
} else if(_isDetail) {
_item.detailUrl = string;
_isDetail = NO;
}
}

You should try parsing the XML file from a stream. You can initialise your NSXMLParser initWithStream: which takes a NSInputStream as argument, it will read and parse data in batches from the stream object.
You can create your NSInputStream with initWithURL: passing the URL from which to download the xml file. When you initialise the NSXMLParser with the stream it will automatically open and read the stream.
This will give you smaller responses more often over time using less memory and hopefully less CPU.
There's a slight hint to follow with this approach:
Apple says that NSXMLParser open the stream and starts reading, but by doing this even if you call:
[parser setDelegate:self] the delegate methods are not called if you don't call [parser parse]. The trick here is to call parse in a GCD block:
xmlParser = [[NSXMLParser alloc] initWithStream:inputStream]; [xmlParser setDelegate:self];
dispatch_block_t dispatch_block = ^(void) {
[xmlParser parse];
};
dispatch_queue_t dispatch_queue = dispatch_queue_create("parser.queue", NULL);
dispatch_async(dispatch_queue, dispatch_block);
dispatch_release(dispatch_queue);
Hope it helps.

Related

Doesn't geting data in right form from a 3 level xml service when using NSXMLParser class

I have to parse a 3 level xml which showing below:-
<Navigation>
<parent>
<parentheader>
<![CDATA[ Home ]]>
</parentheader>
<url>my-profile</url>
<Content>...</Content>
</parent>
<parent>
<parentheader>
<![CDATA[ Exhibiton ]]>
</parentheader>
<child>
<childheader>
<![CDATA[ London Exhibition ]]>
</childheader>
<subchild>London Sub
<url>ezone</url>
<Content>...</Content>
</subchild>
</child>
<child>
<childheader>
<![CDATA[ Asia Exhibition ]]>
</childheader>
<url>exhibition-asia-tour</url>
<Content>...</Content>
</child>
</parent>
</Navigation>
i am implementing the NSXMLParser class and delegates method below is the code:-
.h File
#interface NavigationXMLParser : NSObject<NSXMLParserDelegate>
{
NSXMLParser *xmlParser;
NSMutableDictionary *item,*childDict,*subChildDict;
NSMutableArray *nodesArr,*childArr,*subChildArr;
NSMutableString *parent, *url,*child,*subchild;
NSString *currentElement;
BOOL childBool;
}
-(void) fetchXMLData;
#end
.m implementation file code:-
#implementation NavigationXMLParser
-(void) fetchXMLData
{
xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:[NSURL URLWithString:#"http://exhibitors.gastechkorea.com/admin/XMl_APP_Navigation.aspx"]];
[xmlParser setDelegate:self];
[xmlParser setShouldResolveExternalEntities:NO];
[xmlParser setShouldProcessNamespaces:NO];
[xmlParser setShouldReportNamespacePrefixes:NO];
[xmlParser parse];
}
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
nodesArr =[[NSMutableArray alloc] init];
//[sharedSQLiteObj createFavoriteAgendaTableNamed:#"" withField1:#"" withField2:#"" withField3:#"" withField4:#""];
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
NSString * errorString = [NSString stringWithFormat:#"Unable to download story feed from web site (Error code %i )", [parseError code]];
UIAlertView * errorAlert = [[UIAlertView alloc] initWithTitle:#"Error loading content" message:errorString delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[errorAlert show];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
{
currentElement = [elementName copy];
if ([elementName isEqualToString:#"parent"])
{
item = [[NSMutableDictionary alloc] init];
parent = [[NSMutableString alloc]init];
url = [[NSMutableString alloc]init];
}
else if ([elementName isEqualToString:#"child"])
{
child = [[NSMutableString alloc]init];
childArr = [[NSMutableArray alloc] init];
childDict = [[NSMutableDictionary alloc] init];
url = [[NSMutableString alloc]init];
}
else if ([elementName isEqualToString:#"subchild"])
{
subchild = [[NSMutableString alloc]init];
subChildArr = [[NSMutableArray alloc] init];
subChildDict = [[NSMutableDictionary alloc] init];
url = [[NSMutableString alloc]init];
}
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if ([currentElement isEqualToString:#"parentheader"])
{
[parent appendString:string];
}
else if ([currentElement isEqualToString:#"url"])
{
[url appendString:string];
if (!(child.length ==0))
{
if (subchild.length==0)
{
[childDict setObject:string forKey:#"url"];
}
else{
[subChildDict setObject:string forKey:#"url"];
}
}
}
else if ([currentElement isEqualToString:#"childheader"])
{
[child appendString:string];
if (!(string.length ==0))
{
[childDict setObject:string forKey:#"childheader"];
}
}
else if ([currentElement isEqualToString:#"subchild"])
{
[subchild appendString:[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
if (!([string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].length ==0))
{
[subChildDict setObject:string forKey:#"subchild"];
[subChildArr addObject:[subChildDict copy]];
[childDict setObject:subChildArr forKey:#"subchild"];
subChildDict = nil;
};
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:#"child"])
{
if (!(childDict.count ==0)) {
[childArr addObject:[childDict copy]];
childDict = nil;
}
}
if ([elementName isEqualToString:#"parent"])
{
[item setObject:[parent stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] forKey:#"parent"];
[item setObject:[url stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] forKey:#"url"];
if (!(childArr.count ==0))
{
[item setObject:childArr forKey:#"child"];
}
else
{
[item setObject:#"" forKey:#"child"];
}
//[item setObject:[subchild stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] forKey:#"subchild"];
[nodesArr addObject:[item copy]];
}
}
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
NSLog(#"nodesArr---%#",nodesArr);
}
#end
i got the responsed array from xmlparser class:-
(
{
child = "";
parent = Home;
url = "my-profile";
},
{
child = (
{
childheader = "\n";
}
);
parent = Exhibiton;
url = "exhibition-asia-tour";
},
{
child = (
{
childheader = "\n";
}
);
parent = Calender;
url = "http://www.google.com";
}
)
i am not getting the data in right structure i am wrong some where but didn't find the solution.
i want to get data in the below structure:--
(
{ parent="…."
child=""
url="……"
content="…."
}
{parent ="……"
child = ({ child="……";
subchild= ({
name= "….."
url="….."
content="….."
}
{
…………………….
…..……………..
})
}
{
child="……"
………………
})
)
}
Thanks in advace for your help!!!
it's all about how to make use of the NSXMLParserDelegate methods.
Your XML file can provide you the following informations:
The characters between [CDATA], and the characters between elements.
So you need to implement the following Delegate methods to retrive the informations from the XML file:
This method will get you every string between the [CDATA]:
-(void)parser:(NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock
{
NSMutableString *str=[[NSMutableString alloc]initWithData:CDATABlock encoding:NSUTF8StringEncoding];
[SomeArray addObject:str];
}
And this method will get you every string between elements:
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
// here you need to alloc your string
NSMutableString *parent = [[NSMutableString alloc]init];
[parent appendString:string];
[parent release];
}
after you retrive your data in NSMutableArray and NSMutableString, you can filter your data as you need. But thats how to Parse your XML file.
It's just not that complicated. Good luck ^_^

nsmutablearray elements to nsstring

I want to retrieve elements that are parsed in a NSMutableArray and store them into a NSString variable and then store them in NSMutableArray as NSString (because I want to display the content in a NSComboBox). I tried this but it dosen't work. Can you fix the problem, I can't fix it:
//--this is the parsing code :
- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict {
if ([elementName isEqualToString:#"user"]) {
NSLog(#"user element found – create a new instance of User class...");
if(currentElementValue == nil)
currentElementValue = [NSMutableString string];
else
[currentElementValue setString:#""];
}
else {
currentElementValue = nil;
}
user = [[User alloc] init];
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if (!currentElementValue) {
// init the ad hoc string with the value
currentElementValue = [[NSMutableString alloc] initWithString:string];
} else {
// append value to the ad hoc string
[currentElementValue appendString:string];
if (currentElementValue)
{
currentElementValue = nil;
}
}
NSLog(#"Processing value for : %#", string);
}
- (void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#"users"]) {
// We reached the end of the XML document
return;
NSLog(#"QUIT");
}
if ([elementName isEqualToString:#"userName"]) {
[[self user] setUserName:currentElementValue];
NSLog(#"final step for value: %#", user.userName);
NSLog(#"currentElementName content : %#", currentElementValue);
[currentElementValue release];
NSLog(#"release : %#", currentElementValue);
currentElementValue = nil;
NSLog(#"release : %#", currentElementValue);
}
if ([elementName isEqualToString:#"firstName"]) {
[[self user] setFirstName:currentElementValue];
[currentElementValue release];
currentElementValue = nil;
}
if ([elementName isEqualToString:#"lastName"]) {
[[self user] setLastName:currentElementValue];
[currentElementValue release];
currentElementValue = nil;
}
if ([elementName isEqualToString:#"user"]) {
NSLog(#"\n user=%# \n",user);
[users addObject:user];
NSLog(#"userName test : %#", users);
[user release];
user = nil;
}
}
-(BOOL)parseDocumentWithData:(NSData *)data {
if (data == nil)
return NO;
NSXMLParser *xmlparser = [[NSXMLParser alloc] initWithData:data];
[xmlparser setDelegate:self];
[xmlparser setShouldResolveExternalEntities:NO];
BOOL ok = [xmlparser parse];
if (ok == NO)
NSLog(#"error");
else
NSLog(#"OK");
[xmlparser release];
return ok;
}
// this is the xml file :
<users>
<user>
<userName>mspeller</userName>
<firstName>Mike</firstName>
<lastName>Speller</lastName>
</user>
<user>
<userName>mgdan</userName>
<firstName>Mila</firstName>
<lastName>Gdan</lastName>
</user>
</users>
//-------
NSMutableArray *tabletest= [[NSMutableArray alloc] init];
NSMutableString * result = [[NSMutableString alloc] init];
int i;
for(i=0; i < [users count]; i++){
[result appendString:[NSString stringWithFormat:#"%#",[[users objectAtIndex:i] valueForKey:#"userName"]] ];
NSLog(#"result==%#",result);
[tabletest addObject:result];
}
Based on your link in the comment section I think you're accessing the "userName" property the wrong way. You're trying to access it, as users contains NSDictionary objects. As far as I can see you're adding User objects to the NSMutableArray.
Try the following (I took the liberty to beautify the code a bit):
NSMutableArray *tabletest= [NSMutableArray array];
for (User* user in users)
{
NSString* result = [NSString stringWithFormat:#"%#", user.userName];
NSLog(#"result==%#",result);
[tabletest addObject:result];
}
Please correct me if I totally misunderstood your design.
I don't follow what your intention is, but what your code does at the moment is add the same string [user count] time to the array tabletest as follows:
The line:
[result appendString:[NSString stringWithFormat:#"%#",[[users objectAtIndex:i] valueForKey:#"userName"]] ];
accumulates into result the result of appending each [[users objectAtIndex:i] valueForKey:#"userName"] together - each iteration of the loop adds the next item to the end of result.
The line:
[tabletest addObject:result];
Adds the object referenced by result into the array. This is done once per iteration so the array ends up with [users count] references to the same object. Placing a reference to a mutable string into an array does not place a copy of its current value, just a reference to the string - mutate the string and the mutation is visible through the reference stored in the array.
So the final result of your code is an array of [users count] references to the same mutable string, and that string is the concatenation of all the [[users objectAtIndex:i] valueForKey:#"userName"] values.
What was your intent?
If you are trying to create an array of string representations of [[users objectAtIndex:i] valueForKey:#"userName"] then change the code to:
NSMutableArray *tabletest= [[NSMutableArray alloc] init];
for(int i = 0; i < [users count]; i++)
{
// create a string representation of userName
NSString *result = [NSString stringWithFormat:#"%#",[[users objectAtIndex:i] objectForKey:#"userName"]];
// add the string to the array
[tabletest addObject:result];
}
But maybe your intent is something else?

Parsing XML files with special characters

I try to parse a list of persons and pollute a UITableView with the names. But the persons I want to parse have special character (ä, ö, ü). Now if I start parsing the name "Gött" it is "ött" afterwards. Really strange, any ideas? Thanks a lot!
-(id) loadXMLByURL:(NSString *)urlString
{
tweets = [[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:#"lehrer"])
{
currentTweet = [Tweet alloc];
}
}
- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementname isEqualToString:#"name"])
{
currentTweet.content = currentNodeContent;
}
if ([elementname isEqualToString:#"vorname"])
{
currentTweet.vorname = currentNodeContent;
}
if ([elementname isEqualToString:#"created_at"])
{
currentTweet.dateCreated = currentNodeContent;
}
if ([elementname isEqualToString:#"lehrer"])
{
[tweets addObject:currentTweet];
[currentTweet release];
currentTweet = nil;
[currentNodeContent release];
currentNodeContent = nil;
}
}
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
currentNodeContent = (NSMutableString *) [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
- (void) dealloc
{
[parser release];
[super dealloc];
}
#end
This is normal behaviour - parser:foundCharacters can be called multiple times for one string (and tends to be for accented characters). Your string isn't complete until the end of the element, so store them and use the full string when you get to the end of the block.
It is in the documentation for foundCharacters
Apple developer docs on NSXMLParser
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.
Edit as per question:
the code in general is fine but in the characters function, do
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if(nil == currentNodeContent)
currentNodeContent = [[NSMutableString alloc] initWithString:string];
else
[currentNodeContent appendString:string];
}
then in both didStart and didEnd call a method that checks to see if the string is nil, do whatever it was you were going to do with it in the first place, and then release the string (and null it).
The string is ended at both the start of a new element (ie, the text before an opening <), and at the end (the bit of text before the
As per Woody's answer, this is completely expected. You will need to concatenate the strings from the multiple - (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string calls.
The correct way to do this is as follows:
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if (currentElementContent== nil)
currentElementContent = [[NSMutableString alloc] initWithString:string];
else
currentElementContent = [currentElementContent stringByAppendingString:string];
}
You should always be setting the currentElementContent to nil at the very end of the didEndElement method anyway. An example for this is below:
- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
// Do what you want with the parser here
// Set element content variable to nil
currentElementContent = nil;
}
You may need to replace the variable: currentElementContent with whatever variable you have used in your parser to house the content found between the start and end tags.

String Replacement operations in objective c

I got the result which is in the form of
<ZmaterialGroupList><Matkl>001</Matkl><Text>Metal processing</Text></ZmaterialGroupList>
I need to get the result as 001 Metal processing when i apply the string replacement function upon this it give an exception.Please help me.
Added code from comment:
for(int i=0; i<[soapArray.items count]; i++) {
NSString *str = [soapArray.items objectAtIndex:i];
str = [str stringByReplacingOccurrencesOfString:#"<Matkl>" withString:#""];
}
In this way I wrote but I got an exception like
Invalid argument pass at str
You might do better to use NSXMLParser rather than trying to replace pieces of the XML.
If you update your question to include a bit more explaining your code (specifically the code that deals with soapArray), I should be able explain a bit more as to why your code doesn't work as it is.
Using NSXMLParser
It's important to remember that NSXMLParser just reads the data you give it sequentially, it doesn't use a DOM structure.
Setup
Primarily you need to give your parser something to parse! In my example I get a resource from the bundle and convert it to NSData. There is also however another option to initWithContentsOfURL. Make sure you don't forget to set your delegate!
-(void) parse
{
NSString *file = #"myXMLFile.xml";
NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[file stringByDeletingPathExtension] ofType:[file pathExtension]]];
//If you already have a string:
//NSData* data=[xmlString dataUsingEncoding:NSUTF8StringEncoding];
if (data.length == 0)
{
//No data
return nil;
}
...
}
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
[parser parse];
Parsing
-(void) parserDidStartDocument:(NSXMLParser *)parser
{
//Here you set up any variables you might need while parsing
}
-(void) parserDidEndDocument:(NSXMLParser *)parser
{
//I usually don't have to do anything here but you might need to release some variables here for example.
}
-(void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
//Here is where the bulk of the parsing is done if you are using attributes. I prefer to use attributes as it looks cleaner both in the XML file and the parser.
if ([elementName isEqualToString:#"element1"])
{
//Just an example of what you might want to do
int index = [[attributeDict valueForKey:#"attribute1"] intValue];
NSString *name = [attributeDict valueForKey:#"n"];
[exampleDictionary setValue:name forKey:[NSString stringWithFormat:#"%d", index]];
}
if ([elementName isEqualToString:#"element2"])
{
//We need to know that the next piece of information (from foundCharacters) is for element 2
currentElement = ELEMENT_2;
}
}
//If you haven't used attributes you might find that you have a lot of parsing to do here instead.
-(void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
//Check what this info is for?
if(currentElement == ELEMENT_2)
{
element2Data = [NSString stringWithString:string];
}
}
-(void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:#"element2"])
{
myObject.somethingThatNeedsElement2 = element2;
}
}
Finishing
After finishing it's a good idea to check that nothing went wrong and free up the parser memory. So in your parse method, add this after the call to [parser parse].
if ([parser parserError] != nil)
{
[[[[UIAlertView alloc] initWithTitle:#"Error parsing XML" message:[[parser parserError] localizedDescription] delegate:nil cancelButtonTitle:#"Done" otherButtonTitles:nil] autorelease] show];
}
[parser release];
Consider using an XML parser or you can use NSScanner:
Example:
NSString *wanted;
NSString *fullMessage;
NSString *xml = #"<ZmaterialGroupList><Matkl>001</Matkl><Text>Metal processing</Text></ZmaterialGroupList>";
NSScanner *scanner = [NSScanner scannerWithString:xml];
[scanner scanUpToString:#"<Matkl>" intoString:nil];
[scanner scanString:#"<Matkl>" intoString:nil];
[scanner scanUpToString:#"</Matkl>" intoString:&wanted];
fullMessage = wanted;
[scanner scanUpToString:#"<Text>" intoString:nil];
[scanner scanString:#"<Text>" intoString:nil];
[scanner scanUpToString:#"</Text>" intoString:&wanted];
fullMessage = [fullMessage stringByAppendingFormat:#" %#", wanted];
NSLog(#"fullMessage: '%#'", fullMessage);
NSLog output:
fullMessage: '001 Metal processing'

Parsing simple XML in Objective-C

I have the following very simple XML returned from a webserver which I use ASIHttpRequest to connect to:
<element1>something</element1>
<element2>somethingelse</element2>
<element3>anotherthing</element3>
ASIHttpRequest can return it as NSData or NSString. I need to parse the information, what is the easiest way to do so?
Thanks
There are some XML parsers available for iOS NSXMLParser, libxml2 (DOM and SAX),TBXML,KissXML. You can refer http://www.raywenderlich.com/553/how-to-chose-the-best-xml-parser-for-your-iphone-project to choose best XML Parser (Speed and memory footprint). Easiest would be TBXML. NSXMLParser is easy as well.
NSXMLParser* xmlParser = [[NSXMLParser alloc] initWithData:receivedXMLData];//init NSXMLParser with receivedXMLData
[xmlParser setDelegate:self]; // Set delegate for NSXMLParser
[xmlParser parse]; //Start parsing
[xmlParser release];
//Delegate Methods
//Have a instance variable NSMutableString* currentString; to hold data between elements and NSMutableArray* elementsArray; to hold parsed data
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
elementsArray = [[NSMutableArray alloc] init];
}
//store all found characters between elements in currentString mutable string
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if(!currentString)
{
currentString = [[NSMutableString alloc] init];
}
[currentString appendString:string];
}
//When end of XML tag is found this method gets notified
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if([elementName isEqualToString:#"element1"])
{
[elementsArray addObject:currentString];
[currentString release],currentString=nil;
return;
}
if([elementName isEqualToString:#"element2"])
{
[elementsArray addObject:currentString];
[currentString release],currentString=nil;
return;
}
if([elementName isEqualToString:#"element3"])
{
[elementsArray addObject:currentString];
[currentString release],currentString=nil;
return;
}
[currentString release],currentString =nil;
}
//Parsing has ended
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
NSLog(#"Content of Elements Array: %#",elementsArray);
[elementsArray release],elementsArray=nil;
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
UIAlertView* parseErrorAlert = [[UIAlertView alloc] initWithTitle:#"Parse Error" message:[NString stringWithFormat:"%#",[parseError localizedDescription]] delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[parseErrorAlert show];
[parseErrorAlert release];
}
You have other delegate methods too like parseErrorOccured. Refer http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSXMLParser_Class/Reference/Reference.html
For TBXML : http://tbxml.co.uk/TBXML/API.html
Updated: Implemented parseError delegate method and valid xml example
XML You posted in code is not a valid XML (You can check XML validation online: http://validator.w3.org/#validate_by_input) so it will throw an parseError. Here is valid XML for XML you posted in question:
<?xml version="1.0"?>
<root>
<element1>something</element1>
<element2>somethingelse</element2>
<element3>anotherthing</element3>
</root>
Try also XMLObject. This may helps if you want to keep your cocoa style in your project.