Parsing XML files with special characters - objective-c

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.

Related

NSXMLParser escape Nodes?

I need to escape certain NSXMLnodes, treating them as strings?
for example i have this (Xliff v1.2 type XML file)
<source>Hello <ph id="1">username</ph></source>
I would like to escape the ph node.
Is there an easy way to do this, without pre-scanning the xml and escaping the node beforehand?
So Its actually pretty easy to do exactly this with NSXMLParser and XMLDictionary
Heres the relevant code changes I made to XMLDictionary.m:
- (void)parser:(__unused NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(__unused NSString *)namespaceURI qualifiedName:(__unused NSString *)qName attributes:(NSDictionary *)attributeDict
{
if (![elementNameisEqualToString#"ph"])
{
//normal parsing in here
}
else
{
//manually re-add the element
NSMutableString *nodeString = [NSMutableString stringWithFormat:#"<"];
[nodeString appendString:elementName];
if ([[attributeDict allKeys] count])
{
for (NSString *key in attributeDict)
{
[nodeString appendString:#" "];
[nodeString appendString:key];
[nodeString appendString:#" =\""];
[nodeString appendString:[attributeDict objectForKey:key]];
[nodeString appendString:#"\""];
}
}
[nodeString appendString:#">"];
[self addText:nodeString];
}
- (void)parser:(__unused NSXMLParser *)parser didEndElement:(__unused NSString *)elementName namespaceURI:(__unused NSString *)namespaceURI qualifiedName:(__unused NSString *)qName
{
if(![elementNameisEqualToString#"ph"])
{
//normal parsing in here
}
else
{
[self addText:#"</"];
[self addText:elementName];
[self addText:#">"];
}
}

iPhone app crashes when empty node is parsed

I'm trying to parse an xml and the app crashes if there is an empty node in the xml feed.
What may be the reason for this?
EDIT
my xml looks like this
<Sponsors>
<Sponsor>
<Name>name...</Name>
<About>blah...blah...blah</About>
<Website>http://test.com</Website>
<LogoImage>someImage.jpg</LogoImage>
<smallIcon>someImage.jpg</smallIcon>
<Area/>
<BannerImage/>//->>>this node is empty//
</Sponsor>
</Sponsors>
I'm using NSXML Parser like this.
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if(nil != self.currentsponsorElement){
[self.currentsponsorElement appendString:string];
}
}
Try This:-
inside didStartElement use this code:-
- (void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqualToString:#"Sponsor"])
{
if(!soapResults)
{
soapResults = [[NSMutableString alloc] init]; //declared in .h
}
recordResults = YES;
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSMutableString *)string {
if (string != nil) {
[results setString:#""];
[results appendString: string];
NSLog(#"foundResults: %#",results);
}
}
Without looking at your code, I can only give you a general answer - it sounds like your code is "expecting" a value in the "parser:foundCharacters:" method. Go over your code and see what happens when the characters found are in fact nil. Use try-catch blocks and handle all possible scenarios so your code doen't cause crashes as a result of other things you still haven't thought of.

XML parsing in IOS Shows NULL value in label?

i m doing XML parsing using NSXMLParserDelegate..I m getting null value when i print the value in label...I m using simple xml file got in an internet.You can check what is in that file too.anyone pls help..Here is my code
#import "xmlViewController.h"
#implementation xmlViewController
#synthesize label;
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL *url=[NSURL URLWithString:[NSString stringWithFormat:#"http://www.bonifacedesigns.com/tuts/xmltest.xml"]];
receivedData=[[NSMutableData alloc]initWithContentsOfURL:url];
dataParser=[[NSXMLParser alloc]initWithData:receivedData];
dataParser.delegate=self;
[dataParser parse];
}
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
// if(!xmlString){
//
// }else
// {
// [xmlString setString:#""];
// }
if([elementName isEqualToString:#"elements"])
{
xmlString=[NSMutableString alloc];
[xmlString setString:#""];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
xmlString= (NSMutableString *)[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if([elementName isEqualToString:#"element"]){
NSString *string=xmlString;
[xmlArray addObject:string];
label.text=[NSString stringWithFormat:#"%#",xmlArray];
}
}
#end
This is my XML file....I want to print XML Data Parsed! in my label.Help me with that.I have edited the code and check now
<elements>
<element myData="XML Data Parsed!"/>
</elements>
check this:
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
// if(!xmlString){
//
// }else
// {
// [xmlString setString:#""];
// }
if([elementName isEqualToString:#"elements"])
{
xmlString=[[NSMutableString alloc]initWithCapacity:5];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
xmlString= (NSMutableString *)[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if([elementName isEqualToString:#"element"]){
[attributeDict objectForKey:#"myData"];
[xmlString setString:#""];
NSString *string=xmlString;
[xmlArray addObject:string];
label.text=[NSString stringWithFormat:#"%#",xmlArray];
}
I m getting error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with setString:'
*** First throw call stack:
(0x13ba052 0x154bd0a 0x1362a78 0x13629e9 0x1387970 0x2a26 0x9e1a35 0x5171532 0x9e002d 0x26b8 0xd764e 0x37a73 0x37ce2 0x37ea8 0x3ed9a 0xfbe6 0x108a6 0x1f743 0x201f8 0x13aa9 0x12a4fa9 0x138e1c5 0x12f3022 0x12f190a 0x12f0db4 0x12f0ccb 0x102a7 0x11a9b 0x2238 0x2195 0x1)
terminate called throwing an exception
It seems you have several errors in your code.
The mutable string xmlString isn't properly initialized, on the line :
xmlString = [NSMutableString alloc]; // wrong
This should look like : xmlString = [[NSMutableString alloc] initWithCapacity:X];.
Also, when the parser finds characters, you replace the entire existing mutable string, instead of appending them to the existing content, or try to because according to the code, you simply make a memory address compare and waste the result (==).
// wrong
xmlString == (NSMutableString *)[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
This should look like :
[xmlString appendString:[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
Finally in didEndElement, I'm not sure what you are trying to achieve, but you add the string object reference to an array, and then set the label text to display the content of the array... as long as the mutable array is well allocated and initialized (what I'm really not sure), the label should display at least the array content. But you might consider to "flush" the mutable string ([xmlString setString:#""];) and not allocate it on every element.
EDIT :
The attributeDict maps attribute names as the keys with their values. I guess your attribute value can be accessed via [attributeDict objectForKey:#"myData"];.
I guess that the problem is in this line:
xmlString== (NSMutableString *)[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
Change == to =
Hope that explanation isn't needed.

Can't copy mutable array value into string

i really appreciate for immediate reply and i submit my xml parser methods to you and at last problem is mention.
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
currentElement = [elementName copy];
if([elementName isEqualToString:#"alluser"])
{
objUser = [[Users alloc] init];
}
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if([elementName isEqualToString:#"username"])
{
objUser.userName =[currentElementValue stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSLog(#"%#",objUser.userName);
}
if([elementName isEqualToString:#"password"])
{
objUser.passWord = [currentElementValue stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSLog(#"%#",objUser.passWord);
}
if([elementName isEqualToString:#"user"])
{
[usersArray addObject:objUser];
[objUser release];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if(!currentElementValue)
{
currentElementValue = [[NSMutableString alloc] initWithString:string];
[currentElementValue stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
else
{
[currentElementValue appendString:string];
[currentElementValue stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
}
if([currentElementValue isEqualToString:#"User"])
{
[currentuser appendString:string];
}
if([currentElementValue isEqualToString:#"Pass"])
{
[currentpass appendString:string];
}
}
-(IBAction)backgroundclick:(id)sender
{
[txtpass resignFirstResponder];
[txtusername resignFirstResponder];
}
-(IBAction)returnPressed:(id)sender
{
[sender resignFirstResponder];
}
-(IBAction)LoginClicked:(id)sender
{
int cnt=0;
NSString *currUser = txtusername.text;
// NSString *passWord = txtpass.text;
// int i=0;
for(Users *objUser in usersArray )
{
/// how to get value and how to compare string...
}
return;
}
my mutable array is in this code is (usersArray). i got parse data into the array and i want to check username and password with textfields value if it match with array the certain action is performed. but somehow i cant access data into the string.
You don't give much detail as to what the problem is, so just guessing... For your loop in LoginClicked() you can compare using:
[currUser isEqualToString:objUser.userName]
Or any other string comparison method. But maybe your problem is something else? In your "parser:didStartElement..." method did you mean to check for the "user" element rather than "allUser"? As it is you allocate only one Users object but attempt to parse many and insert them into an array.

NSMutableString issue with NSXMLParser

I am having a heck of a time with this -- I am trying to parse an XML file and set the text in a NSMutableString variable in order to later fill in a label's text as you can see below.
In my .h I have the following (simplified);
NSMutableString *contentsOfCurrentXMLProperty;
#property (nonatomic,retain) NSMutableString *contentsOfCurrentXMLProperty;
In my .m:
-(void) parseData {
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:myData];
[parser setDelegate:self];
[parser parse];
[parser release];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
if ([elementName isEqualToString:#"Title"]) {
NSLog(#"FOUND TITLE!");
contentsOfCurrentXMLProperty = [NSMutableString setString:#""];
}
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#"TITLE"]) {
myLabel.text = [contentsOfCurrentXMLProperty stringByReplacingOccurrencesOfString:#"[br]" withString:#"\n"];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
[self.contentsOfCurrentXMLProperty appendString:string];
}
When I run my app, the data is downloaded and parsed correctly. I run into issues when my observer fires off the event again. The parseData method is called and I get an error that I have traced to the line that reads: "contentsOfCurrentXMLProperty = [NSMutableString setString:#""];
"
What is the proper way to create or init a NSMutableString variable for me to use over and over? When/Where is the best place to release it? How do I essentially clear the variable so that when the observer fires off the parseData method it will again be able to set "contentsOfCurrentXMLProperty"?
-setString: is an instance method, not a class method. If you already have a valid NSMutableString object assigned to contentsOfCurrentXMLProperty and simply want to clear it, then:
contentsOfCurrentXMLProperty = [NSMutableString setString:#""];
should be
[contentsOfCurrentXMLProperty setString:#""];
On the other hand, if you want to assign a new object to contentsOfCurrentXMLProperty, here's one valid way to do it:
self.contentsOfCurrentXMLProperty = [NSMutableString stringWithCapacity:0];