IOS: NSMXLParser with multiple node - ios

I have a file xml as this:
<data>
<first>
<city>
city
</city>
<people>
400
</people>
</first>
<size>
<width>
340
</width>
<height>
120
</height>
</size>
<description>
<temp>
sunny
</temp>
<people>
45
</people>
</description>
<description>
<temp>
cloudy
</temp>
<people>
90
</people>
</description>
I use for parsing this code:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
currentElement = [elementName copy];
if ([elementName isEqualToString:#"first"]) {
firstType = [[NSMutableDictionary alloc] init];
currentCity = [[NSMutableString alloc] init];
currentPeople = [[NSMutableString alloc] init];
}
if ([elementName isEqualToString:#"size"]){
currentSize = [[NSMutableDictionary alloc] init];
width = [[NSMutableString alloc]init];
height = [[NSMutableString alloc]init];
}
if ([elementName isEqualToString:#"description"]){
desc1 = [[NSMutableDictionary alloc] init];
temp1 = [[NSMutableString alloc]init];
people1 = [[NSMutableString alloc]init];
}
if ([elementName isEqualToString:#"description"]){
desc2 = [[NSMutableDictionary alloc] init];
temp2 = [[NSMutableString alloc]init];
people2 = [[NSMutableString alloc]init];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if ([elementName isEqualToString:#"first"]) {
[firstType setObject:currentType forKey:#"city"];
[firstType setObject:currentQuery forKey:#"people"];
[feed addObject:[firstType copy]];
}
if ([elementName isEqualToString:#"size"]){
[currentSize setObject:tempC forKey:#"width"];
[currentSize setObject:tempF forKey:#"height"];
[feed addObject:[currentSize copy]];
}
if ([elementName isEqualToString:#"description"]){
[desc1 setObject:temp1 forKey:#"temp1"];
[desc1 setObject:people1 forKey:#"people1"];
[feed addObject:[desc1 copy]];
}
if ([elementName isEqualToString:#"description"]){
[desc2 setObject:temp1 forKey:#"temp2"];
[desc2 setObject:people1 forKey:#"people2"];
[feed addObject:[desc2 copy]];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
NSLog(#"found");
if ([currentElement isEqualToString:#"city"]){
[currentCity appendString:string];
}
else if ([currentElement isEqualToString:#"people"]) {
[currentPeople appendString:string];
}
else if ([currentElement isEqualToString:#"width"]){
[width appendString:string];
}
else if ([currentElement isEqualToString:#"height"]){
[height appendString:string];
}
else if ([currentElement isEqualToString:#"temp"]){
[temp1 appendString:string];
}
else if ([currentElement isEqualToString:#"temp"]){
[temp2 appendString:string];
}
else if ([currentElement isEqualToString:#"people"]){
[people1 appendString:string];
}
else if ([currentElement isEqualToString:#"people"]){
[people2 appendString:string];
}
}
- (void) parserDidEndDocument:(NSXMLParser *)parser {
NSLog(#"feed:%#",feed);
}
the result of nslog is:
feed:(
{
city = city;
people = 4004590;
},
{
width = 340;
height = 120;
},
{
temp = sunny;
people = "";
},
{ ///???? here there is an empty space
},
{
temp = cloudy;
people = "";
},
{
}
)
Now I don't understand why there is a space between first dictionary of desc 1 and desc2, and I don't know how "people" take the result of people1 and people2 in a single string
Can you help me?

I guess your problem is in duplicated chunks of code such as:
else if ([currentElement isEqualToString:#"temp"]){
[temp1 appendString:string];
}
else if ([currentElement isEqualToString:#"temp"]){
[temp2 appendString:string];
}
In this case your first part of code will executed twice and second never.
Check also another parts of your code, yo have several blocks with this issue.

You need to keep track of whether you are parsing the first occurrence of description within the data tag, or the second. This can easily be done with a boolean (if there are only two), or an integer (for multiple) that indicates which of the tags you are currently processing. Then, in the parser:didEndElement: method, you can assign the accumulated data to the correct dictionary based on the flag/counter.
The other possibility, which I use in my XML parsing, is to accumulate characters for one tag at a time, then when I encounter the closing element for that tag, store the characters into the containing element's Dictionary right then. In other words, when I encounter the endTag for temp, I will immediately assign it to the current description tag's dictionary. Then, when I encounter the ending tag for the description tag itself, I can close out that dictionary, set the flag/increment the counter, and continue on with the next tag that is to be parsed.
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
// ... SNIP ...
if ( [ elementName isEqualToString:#"description" ] )
{
curDescription = [ [ NSMutableDictionary alloc ] init ] ;
}
// ... SNIP ...
accumulatedCharacters = [ [ NSMutableString alloc ] init ]
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
// ... SNIP ...
if ( [ elementName isEqualToString:#"temp" ] )
{
[ curDescription setValue:accumulatedCharacters forKey:#"temp" ] ;
}
if ( [ elementName isEqualToString:#"description" ] )
{
// Save the curDescription object, then clear it for reuse on the next
// occurrence of the tag
[ curDescription release ] ;
curDescription = nil ;
}
// ... SNIP ...
[ accumulatedCharacters release ] ;
accumulatedCharacters = nil ;
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
{
[ accumulatedCharacters appendString:string ] ;
}

Related

Parse the Anchor tags and the content using NSXMLParse

I want to parse the html tags inside the "comparison" node
<comparison>
Amazon.com
($34.36) |
Walmart.com
($34.36) |
Rakuten.com
($34.36) |
BestBuy.com
($34.36)
</comparison>
The output I am getting is:
BestBuy.com ($34.36)
The expected output:
Amazon.com ($34.36)
Walmart.com ($34.36)
Rakuten.com ($34.36)
BestBuy.com ($34.36)
But I want to display all the four items.
CODE
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict {
currentElementValue = [NSMutableString string];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
if ([elementName isEqualToString:#"item"]) {
dealsListObj = [[DealsParsingObjects alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
[currentElementValue appendString:string];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#"short_title"]) {
dealsListObj.itemTitle = currentElementValue;
currentElementValue = nil;
}
else if ([elementName isEqualToString:#"final_price"]) {
dealsListObj.price = currentElementValue;
currentElementValue = nil;
}
//Detail view
else if ([elementName isEqualToString:#"merchant"]) {
dealsListObj.itemMerchant = currentElementValue;
currentElementValue = nil;
}
else if ([elementName isEqualToString:#"getdeal"]) {
dealsListObj.itemGetDeal = currentElementValue;
currentElementValue = nil;
}
//comparison
else if ([elementName isEqualToString:#"comparison"]) {
dealsListObj.comparison = currentElementValue;
currentElementValue = nil;
}
else if ([elementName isEqualToString:#"item"]) {
[resultArray addObject:dealsListObj];
[dealsListObj release];
dealsListObj = nil;
currentElementValue = nil;
}
}
It appears you have a DealsParsingObjects class with an NSMutableArray called commentsArray. You instantiate that array, when the parsing begins reading the <comparison> element.
But when the parser has read a <comparison> element, you assign the value to a property called comparison; it doesn't get added to the array. Being an default NSString property (assumption on my part), it simply gets reassigned each time the parser is done reading a <comparison> element.
Edit:
parser:didStartElement:... is called every time a new element is read. This also holds for the <a> tags. In that method you reset currentElementValue. So for each <a> the value is basically reset to an empty string. Only the last read value remains, which is the value of the last <a> tag plus the trailing text.

NSXMLParser not finding closing tag

Im trying to fetch list of content from xml web source . Im using NSXMLParser atm.
here is the code :
- (void)main
{
self.workingArray = [NSMutableArray array];
self.workingPropertyString = [NSMutableString string];
NSURL *url = [[NSURL alloc]initWithString:#"http://myxmlwebsite.xml"];
NSXMLParser *parser = [[NSXMLParser alloc]initWithContentsOfURL:url];
[parser setDelegate:self];
bool result = [parser parse];
NSLog(#"result is ok for xml parse : %#", result ? #"Yes" : #"No");
if (![self isCancelled])
{
self.appRecordList = [NSArray arrayWithArray:self.workingArray];
SubCategoryViewController *subCategoryViewController;
subCategoryViewController.entries = self.appRecordList;
[subCategoryViewController.tableView reloadData];
}
self.workingArray = nil;
self.workingPropertyString = nil;
self.dataToParse = nil;
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqualToString:kUser])
{
self.workingEntry = [[UserFetchAppRecord alloc] init];
}
_elementsToParse = [[NSArray alloc] initWithObjects:
kid,ktitle, nil];
self.storingCharacterData = [_elementsToParse containsObject:elementName];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
{
if (self.workingEntry)
{
if (self.storingCharacterData)
{
NSString *trimmedString = [self.workingPropertyString stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
[self.workingPropertyString setString:#""];
if ([elementName isEqualToString:kid])
{
self.workingEntry.ids = trimmedString;
NSLog(#"id : %#" , trimmedString);
}
else if ([elementName isEqualToString:ktitle])
{
self.workingEntry.title = trimmedString;
NSLog(#"ktitle : %#" , trimmedString);
}
}
else if ([elementName isEqualToString:kUser])
{
NSLog(#"inside elementName isEqualToString:kUser");
[self.workingArray addObject:self.workingEntry];
NSUInteger self_workingArrayCount = [self.workingArray count];
self.workingEntry = nil;
}
}
}
Now at console if i run program i get these results :
"result is ok for xml parse = true"
"id : 1"
"ktitle : usertitle"
.
.
.
but the result of closing tab no showing in console, this one "inside elementName isEqualToString:kUser".
hows that possible ?
the tag of my xml are like this :
<Main>
<user>
<id></id>
<title></title>
</user>
<user>
<id></id>
<title></title>
</user>
.....
</Main>
Change else if ([elementName isEqualToString:kUser]) in
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
to if ([elementName isEqualToString:kUser]).
self.storingCharacterData become YES when element id or title starts. So when element user ends self.storingCharacterData will be YES, and else if ([elementName isEqualToString:kUser]) wont work.

How to parse SOAP xml response in iPhone programming

I am unable to parse soap response,I have tried many ways but not solved my problem,I need the value of USERNAME and PASSWORD.
I am getting following response after soap request :
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<AuthenticateResponse xmlns="http://tempuri.org/">
<AuthenticateResult>
<DocumentElement>
<Status>
<USERNAME>True</USERNAME>
<PASSWORD>true</PASSWORD>
</Status>
</DocumentElement>
</AuthenticateResult>
</AuthenticateResponse>
</soap:Body>
</soap:Envelope>
Here is my code :
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *) namespaceURI qualifiedName:(NSString *)qName attributes: (NSDictionary *)attributeDict
{
if ( [elementName isEqualToString:#"AuthenticateResponse"]||[elementName
isEqualToString:#"AuthenticateResult"] || [elementName isEqualToString:#"DocumentElement"])
{
statusArray = [[NSMutableArray alloc] init];
}
else if ([elementName isEqualToString:#"Status"])
{
authenticate = [[Authenticate alloc] init];
}
else if ([elementName isEqualToString:#"USERNAME"])
{
currentElementValue = [[NSMutableString alloc] init];
}
else if ([elementName isEqualToString:#"PASSWORD"])
{
currentElementValue = [[NSMutableString alloc] init];
}
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if (currentElementValue)
{
[currentElementValue appendString:string];
}
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI: (NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if( [elementName isEqualToString:#"USERNAME"])
{
authenticate.userName = currentElementValue;
}
else if ([elementName isEqualToString:#"PASSWORD"])
{
authenticate.strAuthenticate = currentElementValue;
}
else if ([elementName isEqualToString:#"Status"]) {
[statusArray addObject:authenticate];
}
}
How can i get the values of username and password,can someone help me to parse this.
Thanks in advance.
Try this:
Add 2 properties to your class.
#property (nonatomic, strong) NSString *elementValue;
#property (nonatomic, strong) NSMutableArray *authenticationValues;
Replace your current implementation by:
- (void)parseXML:(NSData *)xmlData {
self.authenticationValues = [NSMutableArray array];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:xmlData];
parser.delegate = self;
[parser parse];
NSLog(#"Authentication values: %#", self.authenticationValues);
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
// Keep track of the current element value
self.elementValue = string;
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
// If the element was 'username' or 'password', add the value to the authenticationValues array
if ([elementName isEqualToString:#"USERNAME"] ||
[elementName isEqualToString:#"PASSWORD"]) {
[self.authenticationValues addObject:self.elementValue];
}
}
To parse the file perform the method parseXML:
When running this you should see the following output on the console:
Authentication values: (
True,
true
)
which are the values for USERNAME and PASSWORD.
Please see the demo prepared for iOS/OSX using sample web service.
It uses swift - SOAP request creator & response parser.
[DEMO][1]
Done using Alamofire & SWXMLHash library
Thanks

Why does this not parse correctly?

Ok so basically, I am trying to parse a sub-element:
<element>
<sub element 1>
<sub element 2>
<sub element 3>
<element>
The current original document that I want to parse contain as below for example:
sub-element-content1: AB, CD, DE, & SOMETHING DISPLAYED.
I only managed to grab & SOMETHING DISPLAYED. I could not parse the whole sub-element.
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
currentNodeContent = (NSMutableString *)[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
//NSLog(#"CURRENT CONTENT: %#", currentNodeContent);
}
That is the 1st issue. The second issue that I am having is that there are supposed to be 11 items in reference to the above. But it only managed to grab 2 items. Please note that each item contains the sub-elements except that some sub-elements are null
Issues has been added: (null)-(null)-(null)
Issues has been added: sub-element-1 sub-element2 sub-element3
Below is portion of didEndElement:
if ([elementName isEqualToString:#"Issue"]) {
//add currentIssue object into issues array
[issues addObject:currentIssue];
NSLog(#"Issues has been added: %#-%#-%#", currentIssue.sub-element-1, currentIssue.sub-element-2, currentIssue.sub-element-3);
currentIssue = nil;
currentNodeContent = nil;
}
if ([elementName isEqualToString:#"sub element 1"]) {
currentIssue.sub-element-1 = currentNodeContent;
NSLog(#"sub element 1: %#", currentIssue.sub-element-1);
}
if ([elementName isEqualToString:#"sub element 2"]) {
currentIssue.sub-element-2 = currentNodeContent;
NSLog(#"sub element 2: %#", currentIssue.sub-element-2);
}
if ([elementName isEqualToString:#"sub element 3"]) {
currentIssue.sub-element-3 = currentNodeContent;
NSLog(#"sub element 3: %#", currentIssue.sub-element-3);
}
Your advice(s) and/or suggestion(s) are greatly appreciated.
Update 1:
Ok, I have solved the problem.
It appears that "currentIssue = nil" causes the object to lose its contents as I was able to print out the currentNodeContent.
So, by removing, currentIssue, I am now able to see all the supposed elements that I need.
But there is one more problem. I can see all the sub elements now, but there only one sub element left which was grabbed partially which I do not understand why.
For <sub element 1> , sub element 1 is not the elementName . The element name is sub.
- (XMLParserViewController *) initXMLParser {
appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
return self;
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict
{
if([elementName isEqualToString:#"Books"]) {
appDelegate.books = [[NSMutableArray alloc] init];
}
else if([elementName isEqualToString:#"Book"])
{
aBook = [[Books alloc] init];
}
NSLog(#"Processing Element: %#", elementName);
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if(!currentElementValue)
currentElementValue = [[NSMutableString alloc] initWithString:string];
else
[currentElementValue appendString:string];
NSLog(#"Processing Value: %#", currentElementValue);
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if([elementName isEqualToString:#"Books"])
return;
if([elementName isEqualToString:#"Book"])
{
[appDelegate.books addObject:aBook];
aBook = nil;
}
else if([elementName isEqualToString:#"name"])
{
aBook.name=currentElementValue;
}
else if([elementName isEqualToString:#"address"])
{
aBook.address=currentElementValue;
}
else if([elementName isEqualToString:#"country"])
{
aBook.country=currentElementValue;
}
currentElementValue = nil;
NSLog(#"%#",aBook.name);
NSLog(#"%#",aBook.address);
NSLog(#"%#",aBook.country);
}
Try this code eith your elements hope this works for you.... :)
Ok, I have solved the problem.
It appears that "currentIssue = nil" causes the object to lose its contents as I was able to print out the currentNodeContent.
So, by removing, currentIssue, I am now able to see all the supposed elements that I need.
Secondly, to make it work correctly, I initialized the array under "parserDidStartDocument" instead of "parserDidStartElement".

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