Loading Plist as Object - objective-c

Stackoverflow fellows.
I'm trying to load a plist. But then plist's root can be NSArray or NSDictionary. Is there prettier way to determine which one it is (by the header's type of root) NSArray or NSDictionary? Obviously, It is very common pull up file from NSDictionary or NSArray or NSData.
Thanks.

By using the NSPropertyListSerialization class you can deserialize your plist file from an NSData and then with a fast check understand if it's a NSDictionary or a NSArray through isMemberOfClass:
id plist = [NSPropertyListSerialization propertyListWithData:data options:0 format:nil error:nil]
if ([plist isMemberOfClass:[NSArray class]]) {
..
}

Related

How to edit elements in plist file

I have a Settings.plist and I want to edit some values in this file.
My function to edit/writing is:
- (void) setParamWithName: (NSString*) Name withValue: (NSString*) Value {
// get paths from root direcory
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
// get documents path
NSString *documentsPath = [paths objectAtIndex:0];
// get the path to plist file
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"Settings.plist"];
// check to see if Data.plist exists in documents
if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath])
{
// if not in documents, get property list from main bundle
plistPath = [[NSBundle mainBundle] pathForResource:#"Settings" ofType:#"plist"];
}
// read property list into memory as an NSData object
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
NSString *errorDesc = nil;
NSPropertyListFormat format;
// convert static property list into dictionary object
NSDictionary *temp = (NSDictionary *)[NSPropertyListSerialization propertyListFromData:plistXML mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:&errorDesc];
if (!temp)
{
NSLog(#"Error reading plist: %#, format: %d", errorDesc, format);
}
// checking if element exists, if yes overwriting
// if element not exists adding new element
[temp writeToFile:plistPath atomically:YES];
}
This function read and write (with te same values) Settings.plist.
I do not have any idea (my knowledge about objective-c is not enough) how to add new element or edit existing element. Can anyone help mi with this issue?
I think it's easier as you think.
Once you got the path of the file read it into a NSDictionary. Make a mutable copy of that dictionary with mutableCopy and NSMutableDictionary.
Now edit that mutable dictionary as you like (add s.th., remove s.th., edit s.th. and so on).
Now that you're done you can write it back to the old path as you did with temp.
Your main problem is that you're not working with a mutable version of that dicitionary. It'd make your life much easier.

How to convert json string to nsdictionary on json parser framework on objective c

I am trying to convert raw json string to NSDictionary. but on NSDictionary i got different order of objects as on json string but i need exactly same order in NSDictionary as on json string. following is code i have used to convert json string
SBJSON *objJson = [[SBJSON alloc] init];
NSError *error = nil;
NSDictionary *dictResults = [objJson objectWithString:jsonString error:&error];
From NSDictionary's class reference:
The order of the keys is not defined.
So, basically you can't do this when using a standard NSDictionary.
However, this may be a good reason for subclassing NSDictionary itself. See this question about the details.
NSDictionary is an associative array and does not preserve order of it's elements. If you know all your keys, then you can create some array, that holds all keys in correct order (you can also pass it with your JSON as an additional parameter). Example:
NSArray* ordered_keys = [NSArray arrayWithObjects: #"key1", #"key2", #"key3", .., nil];
for(NSString* key is ordered_keys) {
NSLog(#"%#", [json_dict valueForKey: key]);
}
//parse out the json data
NSError* error;
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:responseData //1
options:kNilOptions
error:&error];
NSArray* latestLoans = [json objectForKey:#"loans"]; //2
NSLog(#"loans: %#", latestLoans); //3
Source: Follow this link http://www.raywenderlich.com/5492/working-with-json-in-ios-5
Good tutorial but works only on iOS5

NSData writeToFile writes Plist successfully, but then crashes, giving NSInvalidArgumentException

I am converting a JSON file to a plist using the new NSJSONSerialization class and NSPropertyListSerialization class. I manage to convert my JSON to a Plist without errors, but then, at my last step, when I go to write the plist to my desktop, the program crashes, but AFTER the Plist has been generated!
NSData *data = [[NSData alloc] initWithContentsOfURL:path]; \\(NSURL *)path -->goes to my JSON file
NSMutableDictionary *json = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableContainers
error:nil];
//the following removes all key/object pairs where the object is null, because NSPropertyListSerialization with throw an error if there are null values
for (id __strong object in [json objectForKey:#"terms"]) {
if ([object objectForKey:#"image"] == [NSNull null]) {
[object removeObjectForKey:#"image"];
}
}
//the following NSPropertyListSerialization method returns an NSData
id plist = [NSPropertyListSerialization dataFromPropertyList:(id)json
format:NSPropertyListXMLFormat_v1_0
errorDescription:nil];
NSError *writeToFileError;
[plist writeToFile:#"/Users/kalaracey/Desktop/test.plist"
atomically:YES
encoding:NSUTF8StringEncoding
error:&writeToFileError];
Then, at this last line, an NSInvalidArgumentException is thrown, and crashes my program. However, the plist was successfully generated! I can read it, and all is well, except my program crashes.
Could someone please explain why this crashes, and how I could avoid crashing?
The problem seems to be that the variable plist is type id. Cast it to NSData and you should be fine.
NSData *plist = (NSData *) [NSPropertyListSerialization ...];
As you correctly point out in the comment, NSData should use the writeToFile:atomically: method.

Saving a NSArray

I would like to save an NSArray either as a file or possibly use user defaults. Here's what I am hoping to do.
Retrieve already saved NSArray (if any).
Do something with it.
Erase saved data (if any).
Save the NSArray.
Is this possible, and if so how should I do this?
NSArray provides you with two methods to do exactly what you want: initWithContentsOfFile: and writeToFile:atomically:
A short example might look like this:
//Creating a file path under iOS:
//1) Search for the app's documents directory (copy+paste from Documentation)
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
//2) Create the full file path by appending the desired file name
NSString *yourArrayFileName = [documentsDirectory stringByAppendingPathComponent:#"example.dat"];
//Load the array
NSMutableArray *yourArray = [[NSMutableArray alloc] initWithContentsOfFile: yourArrayFileName];
if(yourArray == nil)
{
//Array file didn't exist... create a new one
yourArray = [[NSMutableArray alloc] initWithCapacity:10];
//Fill with default values
}
...
//Use the content
...
//Save the array
[yourArray writeToFile:yourArrayFileName atomically:YES];
You could implement NSCoding on the objects the array contains and use NSKeyedArchiver to serialize/deserialize your array to disk.
BOOL result = [NSKeyedArchiver archiveRootObject:myArray toFile:path];
The archiver will defer to your NSCoding implementation to get serializable values from each object and write a file that can be read with NSKeyedUnarchiver:
id myArray = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
More info in the serialization guide.
This would seem to be a problem most suited to Core Data as this will deal with all the persistent object data. When you retrieve you data it will return an NSSet, which is unsorted so you will have to have some way of sorting the data in the array such as a unique id number assocaited with each object you create.

Plists and connections

I have an app that needs to connect and receive data, different each time that you click in one tab.
Then to show the data to the user, i use a "element.plist" where i have one array of dictionaries( each dictionary has the info in different strings: name, category, ...). I load the info from this plist.
I would like then, to continue using the same structure. Each time i receive the connection data:
delete the content in the plist
save the new content (I can do this in the parser method, each time that i have one object with all the information)
Read the info like i'm doing now.
The step that i can't do is the second.
thanks
I'm not sure I completely understand your question,
but I'll try to help.
below is some apple sample code that saves a plist when an application is exiting.
the second line sets the name of the plist file:
NSString *bundlePath = the application directory + "Data"
the third line defines a dictionary with all the data to be saves:
NSDictionary *plistDict
the fourth line formats this dictionary as property list data:
NSData *plistData
which then gets saved as Data.plist
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
NSString *errorDesc;
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:#"Data" ofType:#"plist"];
NSDictionary *plistDict = [NSDictionary dictionaryWithObjects:
[NSArray arrayWithObjects: personName, phoneNumbers, nil]
forKeys:[NSArray arrayWithObjects: #"Name", #"Phones", nil]];
NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:plistDict
format:NSPropertyListXMLFormat_v1_0
errorDescription:&errorDesc];
if (plistData)
{
[plistData writeToFile:bundlePath atomically:YES];
}
else {
NSLog(errorDesc);
[errorDesc release];
}
return NSTerminateNow;
}
You can find this information in the Property list programming guide
Mey,
I'm not sure that I understand your statement about having an empty plist. I assume that you mean that if you read back the plist file that you created, it is null when you print it out. Suggesting that you are writing out an empty file or not reading correct or ...
I further assume that your intent is to replace the existing plist contents by a new plist while keeping the same name.
And caveat emptor - I'm new to Objective C etc. Here is a way to do that which I think you are trying to do.
// Implement viewDidLoad to do additional setup after loading the view,
// typically from a nib.
- (void)viewDidLoad {
NSBundle *bundle = [NSBundle mainBundle];
NSString *plistPath = [bundle pathForResource:#"TmpPList" ofType:#"plist"]; //Not NARC
//NSLog(#"plistPath : %#", plistPath);
//My plist is a simple array, but it could be an array of dictionary objects etc
NSMutableArray *arrayFromPList = [[NSMutableArray alloc] initWithContentsOfFile:plistPath]; //NARC
//NSLog(#"arrayFromPList : %#", arrayFromPList);
//Delete the arrays contents and put new contents
[arrayFromPList removeAllObjects];
//NSLog(#"arrayFromPList : %#", arrayFromPList);
//[arrayFromPList addObjectsFromArray:[NSArray arrayWithObjects:#"A", #"B", "#C", nil]];
//NSLog(#"arrayFromPList : %#", arrayFromPList);
[arrayFromPList setArray:[NSMutableArray arrayWithObjects:#"A", #"B", #"C", #"D", #"E", #"F", nil]];
//NSLog(#"arrayFromPList : %#", arrayFromPList);
/* */
//Write it out to the original file name
[arrayFromPList writeToFile:plistPath atomically:YES];
NSMutableArray *newArray = [[NSMutableArray alloc] initWithContentsOfFile:plistPath]; //NARC
NSLog(#"newArray : %#", newArray);
[arrayFromPList release];
[newArray release];
}