String Replacement operations in objective c - 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'

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:#">"];
}
}

XML Parsing issue in Xcode?

I am making a mac application, and I need it to parse a local xml file and display it into a tableView. For some reason, I get a blank row in my tableView, which makes no sense, as it has found characters that are in my xml. Here is my code:
- (void)parserDidStartDocument:(NSXMLParser *)parser{
NSLog(#"found file and started parsing");
}
- (void)parseXMLFileAtURL:(NSString *)URL
{
//you must then convert the path to a proper NSURL or it won't work
NSURL *xmlURL = [NSURL URLWithString:URL];
// here, for some reason you have to use NSClassFromString when trying to alloc NSXMLParser, otherwise you will get an object not found error
// this may be necessary only for the toolchain
rssParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];
// Set self as the delegate of the parser so that it will receive the parser delegate methods callbacks.
[rssParser setDelegate: self];
// Depending on the XML document you're parsing, you may want to enable these features of NSXMLParser.
[rssParser setShouldProcessNamespaces:NO];
[rssParser setShouldReportNamespacePrefixes:NO];
[rssParser setShouldResolveExternalEntities:NO];
[rssParser parse];
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
NSString * errorString = [NSString stringWithFormat:#"Unable to download XML feed (Error code %i )", [parseError code]];
NSLog(#"Error parsing XML: %#", errorString);
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
//NSLog(#"found this element: %#", elementName);
currentElement = [elementName copy];
if ([elementName isEqualToString:#"event"]) {
// clear out our story item caches...
item = [[NSMutableDictionary alloc] init];
date = [[NSMutableString alloc] init];
opponent = [[NSMutableString alloc] init];
location = [[NSMutableString alloc] init];
time = [[NSMutableString alloc] init];
games = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
NSLog(#"ended element: %#", elementName);
if ([elementName isEqualToString:#"event"]) {
// save values to an item, then store that item into the array...
[item setObject:date forKey:#"date"];
[item setObject:opponent forKey:#"opponent"];
[item setObject:location forKey:#"location"];
[item setObject:time forKey:#"time"];
[item setObject:games forKey:#"games"];
NSMutableArray *stories = [[NSMutableArray alloc] init];
[stories addObject:[item copy]];
[arrayController addObject:[NSDictionary dictionaryWithObjectsAndKeys:
date, #"date",
opponent, #"opponent",
location, #"location",
time, #"time",
games, #"games"
, nil]];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
//NSLog(#"found characters: %#", string);
// save the characters for the current item...
if ([date isEqualToString:#"date"]) {
[date appendString:string];
} else if ([opponent isEqualToString:#"opponent"]) {
[opponent appendString:string];
} else if ([location isEqualToString:#"location"]) {
[location appendString:string];
} else if ([time isEqualToString:#"time"]) {
[time appendString:string];
} else if ([games isEqualToString:#"games"]) {
[games appendString:string];
}
}
- (void)parserDidEndDocument:(NSXMLParser *)parser {
NSLog(#"all done!");
[tabelview reloadData];
}
When i remove my adding part, where it adds item to the arraycontroller, and add
[arrayController addObject:stories]; I get a buch of ('s
If there is anything else you need, do not just down-vote, and instead tell me. Thanks!
Here is my xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xmlData>
<event>
<date>date here</date>
<opponent>opponent here</opponent>
<location>location here</location>
<time>time here</time>
<games>games here</games>
</event>
<event>
<date>date here</date>
<opponent>opponent here</opponent>
<location>location here</location>
<time>time here</time>
<games>games here</games>
</event>
<event>
<date>date here</date>
<opponent>opponent here</opponent>
<location>location here</location>
<time>time here</time>
<games>games here</games>
</event>
<event>
<date>date here</date>
<opponent>opponent here</opponent>
<location>location here</location>
<time>time here</time>
<games>games here</games>
</event>
</xmlData>
The error is in your parser. Please revise the logic. You are not using your object item when filling your table view array. Also, you are not catching the text in between the XML elements and are not assigning them to the appropriate variables.
Note the following:
When you enter an element, keep track of which element you are in currently
When you find characters you have to fill the appropriate attribute variable according to which element you are in
When you finish the event element, you should add your item with all its filled in keys into your data array.
You need to know how to use array. You are allocating one array stories. But then not using. Please Check your didEndElement method.
Make one class of Event, create .h and .m file and then create properties of your all element and then add whole object of Event class into an array. That array you can in appHandler or Single ton class.
Check this thing. May it help you.

How to avoid the XML parser go to the nested tag, or only allow the outer tag only in Objective C?

Here is a simple XML:
<MSG>
<ID>123<ID>
<Node>
<ID>456<ID>
</Node>
</MSG>
And I got a parser which is subclass of ParentParser and implements NSXMLParserDelegate
The ParentParser is something like this:
- (id)initWithXmlString:(NSString *)xmlString
{
if ( (self = [super init]) ) {
NSString *str = [[NSString alloc] initWithString:xmlString];
self.xml = str;
[str release];
self.storingData = NO;
self.receiveString = [NSMutableString string];
self.elementsToParse = [NSArray array];
}
return self;
}
And this is how I parse the XML:
- (void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName{
if (self.m_oMyObject) {
if (self.storingData) {
NSString *tempStr = [self.receiveString stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
[self.receiveString setString:#""];
if ([elementName isEqualToString:kID]) {
//Go in twice
}
//============ Code skips ===================
As you can see, the kID, String ID had been parse twice. But I would like to have the 123 only, not the 456, which is inside the <Node> tag, how can I fix it? Thanks.
I don't think that you can make NSXMLParser skip nested elements, but you could keep track of the current level by incrementing an instance variable level in parser:didStartElement:... and decrementing it in parser:didEndElement:....

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 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.