XCODE NSXML changing the element value - iphone

I have a very simple xml file by name options.xml
<Dat>
<Name>Tom</Name>
<Option>1</Option>
</Dat>
Using NSXML I am trying to change "Tom" to "Jim" and save the file. How can I do that. I read many document and there is no straight forward solution. Can some one help me with the code ?
update: I ended up in trying with Gdatasxml
-(void)saveToXML
{
NSString* path = [[NSBundle mainBundle] pathForResource:#"options" ofType:#"xml"];
NSData *xmlData = [[NSMutableData alloc] initWithContentsOfFile:path];
NSError *error;
GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:xmlData options:0 error:&error];
GDataXMLElement *rootElement = [GDataXMLElement elementWithName:#"Dat"];
NSArray *mySettings = [doc.rootElement elementsForName:#"Dat"];
for (GDataXMLElement *mySet in mySettings)
{
NSString *name;
NSArray *names = [mySet elementsForName:#"Name"];
if (names.count > 0)
{
GDataXMLElement *childElement = (GDataXMLElement *) [names objectAtIndex:0];
name = childElement.stringValue;
NSLog(childElement.stringValue);
[childElement setStringValue:#"Jim"];
}
}
[xmlData writeToFile:path atomically:YES];
}
But this is not saving the data. Help.

Editing XML is a little difficult in iOS. You need to parse the original xml to a model and then form the xml.
You can make use of 3rd party library such as GDataXML for forming XML from a data source.
//Edited user info saved in a dictionary
NSDictionary *dictionary = #{#"Name": #"Jim", #"Option":#"1"};
GDataXMLElement *rootElement = [GDataXMLElement elementWithName:#"Dat"];
[dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
GDataXMLElement *element = [GDataXMLElement elementWithName:key stringValue:obj];
[rootElement addChild:element];
}];
//xml document is formed
GDataXMLDocument *document = [[GDataXMLDocument alloc]
initWithRootElement:rootElement];
NSData *xmlData = document.XMLData;
NSString *filePath = [self savedXMLPath];
//XML Data is written back to a filePath
[xmlData writeToFile:filePath atomically:YES];

Create a class that is essentially an XML "node". Then in your parser setup a system of these XML nodes in the same fashion as you read them. Then search through that body and find the element that you would like to change. Change it. Then write a function that goes through these "node" objects and writes a new NSString in XML format and save that string to file. There is no real easy way that I know of to write XML files. I'm sure someone has a library out there to do it, but I had very complex XML's to deal with so I wrote my own. If you would like specific code let me know and I can try to give you parts of what you may need.

You Can use GDATAXML for changing XML node
Here is Working Code snippet
NSString *XMLString = #"<Dat><Name>Tom</Name><Option>1</Option></Dat>";
NSError *error = nil;
GDataXMLElement *newElement = [[GDataXMLElement alloc] initWithXMLString: XMLString error: &error];
NSLog(#"New element: %# error: %#", newElement, error);
if(nil == error)
{
GDataXMLElement *childElement = [[newElement elementsForName: #"Name"] objectAtIndex: 0];
[childElement setStringValue:#"Jim"];
childElement = [[newElement elementsForName: #"Option"] objectAtIndex: 0];
[childElement setStringValue:#"2"];
}
NSLog(#"New element now: %#", newElement);
Check by using this code snippet

Related

How to separate JSON response in iphone

I have an application in which I am having a json response like this.
{"success":"true","message":"You have logged in","pserial":"1"} and I am separating with ":".
And I am getting data like this pSerial:"1"} but I want only 1 value.
NSURL *url = [NSURL URLWithString:strUrl];
NSData *respData = [NSData dataWithContentsOfURL:url];
NSString *strResp = [[NSString alloc]initWithData:respData encoding:NSUTF8StringEncoding];
NSString *approvalString = [[strResp componentsSeparatedByString:#":"] objectAtIndex:3];
NSLog(#"pSerial:%#",approvalString);
for Example :
SBJsonParser *jsonPar = [[SBJsonParser alloc] init];
NSError *error = nil;
NSArray *jsonObj = [jsonPar objectWithString:jsonString error:&error];
id jsonObj = [jsonPar objectWithString:jsonString error:&error];
if ([jsonObj isKindOfClass:[NSDictionary class]])
// treat as a dictionary, or reassign to a dictionary ivar
else if ([jsonObj isKindOfClass:[NSArray class]])
// treat as an array or reassign to an array ivar.
Then get the value :
NSMutableArrary *userMutArr = [NSMutableArray array];
for (NSDictionary *dict in jsonObj)
{
User *userObj = [[[User alloc] init] autorelease];
[userObj setFirstName:[dict objectForKey:#"firstName"]];
[userObj setLastName:[dict objectForKey:#"lastName"]];
[userObj setAge:[[dict objectForKey:#"age"] intValue]];
[userObj setAddress:[dict objectForKey:#"address"]];
[userObj setPhoneNumbers:[dict objectForKey:#"phoneNumber"]];
[userMutArr addObject:userObj];
}
Hope you will understand. and read some Documents. it will help you.
It looks like you are in need of JSON Parsing. Here, what you want is JSON Parsing, not separating data from the JSON Response. JSON is the format of the data in which data is formatted in Key-Value pairs. You can fetch the "Value" of any object using the "Key".
Your first two lines are correct.
NSURL *url = [NSURL URLWithString:strUrl];
NSData *respData = [NSData dataWithContentsOfURL:url];
Now, you can parse JSON Response like this :
NSError* error;
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:respData options:kNilOptions error:&error];
NSString *pSerial = [json objectForKey:#"pserial"];
This will give you the value of "pserial" from your response. Similarly, you can get the values for "success" and "message". You can check it using this line :
NSLog(#"pserial :: %#",pserial);
You need to parse the JSON Response String, you can use any JSON parser like:
https://github.com/stig/json-framework/
And in your code do:
NSString *strResp = [[NSString alloc]initWithData:respData encoding:NSUTF8StringEncoding];
NSDictionary *ResponseDictionary = [strResp JSONValue];
NSString * pSerial = (NSString*)[ResponseDictionary objectForKey:#"pserial"];
Dont separation by ":" just use JSONValue your response is like
// {"success":"true","message":"You have logged in","pserial":"1"}
// with SBJsonParser parse your object like this
NSDictionary *responseJson = [YOUR-OBJECT JSONValue];
Note: dont forget to add Json header file
Its better you use any OpenSource Json Parser
Here is a stack post Comparison of different Json Parser for iOS

CHCSVParser - how to load from webserver

I would like to parse csv from webserver which gets updated everyday.I am using the csvparser from this link https://github.com/davedelong/CHCSVParser and I am using this code:
NSError *err = [[[NSError alloc] init] autorelease];
NSString *lunchFileURL = [[NSString stringWithFormat:#"http://www.somewhere.com/LunchSpecials.csv"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *lunchFile = [NSString stringWithContentsOfURL:[NSURL URLWithString:lunchFileURL] encoding:NSUTF8StringEncoding error:&err];
CHCSVParser *p = [[CHCSVParser alloc] initWithContentsOfCSVString:lunchFile usedEncoding:&encoding error:nil];
I get this error :
No visible #interface for 'CHCSVParser' declares the selector 'initWithContentsOfCSVString:usedEncoding:error:'
I checked this link Load remote csv into CHCSVParser and its not working .I am a noob to ios ,Please let me know how to fix this .Really Appreciate the help.Thanks in Advance.
It should probably be:
NSError *err;
NSString *lunchFileURL = [[NSString stringWithFormat:#"http://www.somewhere.com/LunchSpecials.csv"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *lunchFile = [NSString stringWithContentsOfURL:[NSURL URLWithString:lunchFileURL]
encoding:NSUTF8StringEncoding
error:&err];
// if you're going to create a CHCSVParser yourself, the syntax is:
CHCSVParser *p = [[CHCSVParser alloc] initWithCSVString:lunchFile
encoding:NSUTF8StringEncoding
error:&err];
// or, if you're going to use NSArray+CHCSVAdditions.h, the syntax might be:
NSArray *array = [[NSArray alloc] initWithContentsOfCSVString:lunchFile
encoding:NSUTF8StringEncoding
error:&err];
Note:
You don't need to alloc/init the NSError object; these classes that take a NSError ** parameter will create the autorelease error object for you if they encounter an error; otherwise they leave it alone;
The CHCSVParser class method is not initWithContentsOfCSVString, but rather initWithCSVString;
Alternatively, if you use the NSArray class extension, then the syntax is arrayWithContentsOfCSVString or initWithContentsOfCSVString; and
You specified an encoding of &encoding, but this parameter is not a pointer, so I don't see how that can possibly be right; I've just specified the encoding.
I assume you want to use the NSArray+CHCSVAdditions category method initWithContentsOfCSVString or arrayWithContentsOfCSVString (which gives you an autorelease object), not the CHCSVParser, but it's up to you.

Read/Write plist on iPhone iOS 5

I am studying iPhone development and facing a problem with a reading/writing plist file. I followed an example from a iPhone development book but keep getting an error message when running.
The error message says : 2012-04-26 00:21:09.759 FileHandling[5915:207] -[__NSCFDictionary addObject:]: unrecognized selector sent to instance 0x685ac40
Here is the example code (it seems fine to me...though):
NSString *plistFileName = [[self documentPath] stringByAppendingPathComponent: #"Apps.plist"];
NSLog(#"Where is the file? => %#", plistFileName);
if ([[NSFileManager defaultManager] fileExistsAtPath:plistFileName]) {
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:plistFileName];
for (NSString *category in dict) {
NSLog(#"%#", category);
NSLog(#"=========");
NSArray *titles = [dict valueForKey:category];
for (NSString *title in titles) {
NSLog(#"%#", title);
}
}
} else {
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"Apps" ofType: #"plist"];
NSLog(#"%#", plistPath);
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile: plistPath];
NSLog(#"Let's take a look : %#", dict);
NSMutableDictionary *copyOfDict = [dict mutableCopy];
NSLog(#"Let's look at the mutable dictationary : %#", copyOfDict);
NSArray *categoriesArray = [[copyOfDict allKeys] sortedArrayUsingSelector: #selector(compare:)];
for (NSString *cateogry in categoriesArray) {
NSArray *titles = [dict valueForKey: cateogry];
NSMutableArray *mutableTitles = [titles mutableCopy];
[mutableTitles addObject: #"New App Title"];
[copyOfDict setObject: mutableTitles forKey:cateogry];
}
NSString *fileName = [[self documentPath] stringByAppendingPathComponent: #"Apps.plist"];
[copyOfDict writeToFile: fileName atomically:YES];
}
According to the error message, the problem is occurring in the call to addObject: on an __NSCFDictionary. This means that, at runtime, a dictionary received a message to add an object.
However, in this code snippet, addObject: is apparently being sent to an NSMutableArray. This probably means that each object titles you're retrieving from dict in the last for-loop is not an array, but in fact another dictionary, that your code is simply referring to as an array.
Indeed, your code does seem well-formed, so check the well-formedness of your source plist; open it up in a plain text editor. Also, you use a ton of logging, so confirm this way: in the output, dictionaries (including the root entry) are denoted by {curly = braces}, where arrays are denoted by (round parentheses).

Parsing JSON to get Google Reader Label information

I am trying to make a google reader app. I am able to get the subscription list in JSON format like this:
{"subscriptions":[{"id":"feed/http://aspn.activestate.com/ASPN/Cookbook/Python/index_rss","title":"ActiveState Code: Python recipes","categories":[{"id":"user/014533032765194560dwd0/label/Programming","label":"Programming"}],"sortid":"E6312EFB","firstitemmsec":"1258141669516","htmlUrl":"http://code.activestate.com/recipes/langs/python/"},
I am interested in getting the label value (in the above case "Programming") into an array. Here is my current code:
-(BOOL)parsedSuccess {
SBJsonParser *parser = [[SBJsonParser alloc]init];
if (!receivedData) {
[self getSubscriptionList:GOOGLE_READER_SUBSCRIPTION_LIST];
}
NSMutableString *body = [[NSMutableString alloc]initWithData:receivedData encoding:NSUTF8StringEncoding];
if (body) {
NSArray *feeds = [parser objectWithString:body error:nil];
NSDictionary *results = [body JSONValue];
NSArray *subs = [results valueForKey:#"subscriptions"];
NSString *subTitles;
for (NSDictionary *title in subs){
subTitles = [title objectForKey:#"categories"];
NSLog(#"%#",subTitles);
}
}
return YES;
}
Can someone help me in getting the label values?
[[[[[result valueforkey:#"subscription"]objectatindex:0]valueforkey:#"categories"]objectatindex:intvalue]valueforkey:#"label"];
I just helped to make logic. Be sure to check for spelling mistakes before implementing.

warning: Attempting to create USE_BLOCK_IN_FRAME variable with block that isn't in the frame

What does mean? I get this error when trying to iterate through a file in Cocoa obj-c.
I can't find any information on the web.
Would appreciate some help. Thanks.
EDIT
I've been following this tutorial (link) to preload Core Data. I've tried creating a Cococa application and have also tried doing this from within my iPhone app. I think all my setup code for Core Data is fine. Whenever this method is called I get EXEC BAD ACCESS.
- (void)loadInitialData
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// name ZSTREET_1 ZSTREET_2 ZCITY ZZIP ZURL ZTEL latitude longitude
NSString *path = [[NSBundle mainBundle] pathForResource:#"placesdata" ofType:#"txt"];
NSString *fileString = [NSString stringWithContentsOfFile:path]; // reads file into memory as an NSString
NSArray *lines = [fileString componentsSeparatedByString:#"\r"]; // each line, adjust character for line endings
NSManagedObjectContext *context = [self managedObjectContext];
for (NSString *line in lines)
{
NSLog(line);
NSString* string = [[NSString alloc] initWithUTF8String:line];
NSArray *parts = [string componentsSeparatedByString:#"\t"];
// value mapping
NSString *name = [parts objectAtIndex:0];
NSString *street_1 = [parts objectAtIndex:1];
NSString *street_2 = [parts objectAtIndex:2];
NSString *city = [parts objectAtIndex:3];
NSString *zip = [parts objectAtIndex:4];
NSString *url = [parts objectAtIndex:5];
NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *latitude = [f numberFromString:[parts objectAtIndex:6]];
NSNumber *longitude = [f numberFromString:[parts objectAtIndex:7]];
[f release];
// splitting the parts to create the objects
Place *place = (Place *)[NSEntityDescription insertNewObjectForEntityForName:#"Place" inManagedObjectContext:context];
Address *address = (Address *)[NSEntityDescription insertNewObjectForEntityForName:#"Address" inManagedObjectContext:context];
Location *location = (Location *)[NSEntityDescription insertNewObjectForEntityForName:#"Location" inManagedObjectContext:context];
// set attributes
[place setValue:name forKey:#"name"];
[address setValue:street_1 forKey:#"street_1"];
[address setValue:street_2 forKey:#"street_2"];
[address setValue:city forKey:#"city"];
[address setValue:zip forKey:"#zip"];
[address setValue:url forKey:#"url"];
[location setValue:latitude forKey:#"latitude"];
[location setValue:longitude forKey:#"longitude"];
// link the objects together
[place setValue:address forKey:#"address"];
[place setValue:location forKeyPath:#"address.location"];
[string release];
}
NSLog(#"Done initial load");
NSError *error;
if (![context save:&error]) {
NSLog(#"Error saving: %#", error);
}
[context release];
[pool drain];
}
For other people running into this problem with entirely different code, this is a bit of a red herring.
The warning is from the debugger itself. The debugger creates a struct containing info for each object in the system. After the EXC_BAD_ACCESS, it tried to create one of these but was unable to. Note that this is a warning and not an error so it may even be expected in situations like this.
In any event, the details surrounding this don't matter. You've got to find the source of your EXC_BAD_ACCESS. A common cause is trying to access an object after it has been released.
For someone who comes across this in the future, I got this problem because I was doing this too much:
NSString* aString = #"";
for(int i=0; i<someLargeNumber; i++) {
aString = [aString stringByAppendingFormat:#"..."];
}
Once I switched to using NSMutableString, the problem was resolved.
This line is wrong and should produce a compiler warning:
NSString* string = [[NSString alloc] initWithUTF8String:line];
The method initWithUTF8String: expects an UTF-8 encoded C string and not a NSString object.
Before you continue you should fix all compiler warnings! And you also should check that the parts array actually contains as many objects as you expect. You also need to use a format string with NSLog, you might even crash there if your line contains any % characters.
I happen to run into EXC_BAD_ACCESS on a fairly regular basis (which is not a good thing), because we haven't enabled ARC yet.
I find the best way to track those errors down by using the Profiler in Zombie modus. This post shows you how to "hunt for zombies":
How do I set up NSZombieEnabled in Xcode 4?