Parse Plist (NSString) into NSDictionary - objective-c

So I have a plist structured string, that get dynamically (not from the file system). How would I convert this string to a NSDictionary.
I've tried converting it NSData and then to a NSDictionary with NSPropertyListSerialization, but it returns "[NSCFString objectAtIndex:]: unrecognized selector sent to instance 0x100539f40" when I attempt to access the NSDictionary, showing that my Dictionary was not successfully created.
Example of the NSString (that is the plist data):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Key1</key>
<dict>
<key>Test1</key>
<false/>
<key>Key2</key>
<string>Value2</string>
<key>Key3</key>
<string>value3</string>
</dict>
</dict>
</plist>
Thanks!

See Serializing a Property List
NSData* plistData = [source dataUsingEncoding:NSUTF8StringEncoding];
NSString *error;
NSPropertyListFormat format;
NSDictionary* plist = [NSPropertyListSerialization propertyListWithData:plistData mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&error];
NSLog( #"plist is %#", plist );
if(!plist){
NSLog(#"Error: %#",error);
[error release];
}

Try this:
NSData * data = [yourString dataUsingEncoding:NSUTF8StringEncoding];
NSString *errorDesc = nil;
NSPropertyListFormat format;
NSDictionary * dict = (NSDictionary*)[NSPropertyListSerialization
propertyListFromData:data
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:&format
errorDescription:&errorDesc];

I've tried converting it NSData and then to a NSDictionary with NSPropertyListSerialization, but it returns "[NSCFString objectAtIndex:]: unrecognized selector sent to instance 0x100539f40" when I attempt to access the NSDictionary, showing that my Dictionary was not successfully created.
No, it shows no such thing. What it shows is that you tried to treat a string as an array. You'd need to determine where in the plist you were trying to get an array and why there was a string where you expected an array—i.e., whether you created the plist incorrectly (putting a string into it where you meant to put an array) or are examining it incorrectly (the presence of a string is correct; your subsequent expectation of an array is wrong).

Related

How do I write a Dictionary to an array in a plist?

I have a plist that is structured like this:
<dict>
<key>memos</key>
<array>
<dict>
<key>title</key>
<string>First Memo</string>
<key>content</key>
<string>Testing one two three</string>
<key>category</key>
<string>testing</string>
</dict>
<dict>
<key>title</key>
<string>Test</string>
<key>content</key>
<string>This is a test memo.</string>
<key>category</key>
<string>testing</string>
</dict>
</array>
</dict>
I'm reading it into an array like this:
self.memos = [NSMutableArray array];
NSString *path = [[NSBundle mainBundle] pathForResource:#"Memos" ofType:#"plist"];
NSDictionary *tempDictionary = [NSDictionary dictionaryWithContentsOfFile:path];
NSArray *tempArray = [tempDictionary objectForKey:#"memos"];
for(id dict in tempArray) {
NSString *title = [dict objectForKey:#"title"];
NSString *content = [dict objectForKey:#"content"];
NSString *category = [dict objectForKey:#"category"];
Memo *m = [[Memo alloc] initWithTitle:title content:content category:category];
[self.memos addObject:m];
}
My question is: how do I add a new Memo (a dictionary of three strings) to the plist (specifically, the array of Memo dictionaries in my plist)?
You have to grab the plist in a NSDictionary, then read the Memos array into a NSMutableArray, add a object to it, then save the mutable memos array as memos in the dictionary and write it back to the plist file.
Change this line:
NSArray *tempArray = [tempDictionary objectForKey:#"memos"];
to
self.memos = [tempDictionary objectForKey:#"memos"];
Hope it helps...
and make sure you write back the self.memos data back to the file.
Also note you cannot edit a file in the bundle. You need to ensure that you copy it over to the documents directory before you start editing it and save it.
Try this:
[tempArray addObject:NewDictionary That you want to insert];
then add this temp array to TempDictionary.
[tempDictionary setObject:tempArray forKey:#"memos"];
and then save tempdictionary to file.
[tempDictionary writeToFile:path atomically:YES];
Hope it Helps!!

iOS 6: Get strings into array from file line by line

I searched the web but everything I can find doesn't work because it uses old code or something...
What filetype would you choose to import a list of words? txt? (about 200 words)
How can I get all of the words out of the file and into an array?
I want to output a random word out of the file into a label.
I tried:
NSString* path = [[NSBundle mainBundle] pathForResource:#"filename"
ofType:#"txt"];
NSString* content = [NSString stringWithContentsOfFile:path
encoding:NSUTF8StringEncoding
error:NULL];
NSArray *arr = [content componentsSeparatedByString:#"\n"];
self.biglabel.text = arr[arc4random() % arr.count];
Thanks in advance!
Michael
edit:
works now. I didn't have plain txt files. I renamed a rtf file and that didn't work.
XCode 4.3.3
You could store it as a plist and then turn that directly into an array.
NSString* path = [[NSBundle mainBundle] pathForResource:#"filename" ofType:#"plist"];
NSArray* arr = [NSArray arrayWithContentsOfFile:path];
Your plist would look something like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<string>...</string>
...
</array>
</plist>

Reading NSMutableArray from File

I am trying to read an 2 NSMutableArrays from file. I am saving and loading as such:
SAVE:
NSMutableDictionary *saveDict = [NSMutableDictionary dictionary];
[saveDict setValue:name forKey:#"name"];
[saveDict setValue:last_episodue forKey:#"whereat"];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingString:#"/ShopFile.sav"];
[saveDict writeToFile:filePath atomically:YES];
LOAD:
name = [[NSMutableArray alloc]init];
last_episodue = [[NSMutableArray alloc]init];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingString:#"/ShopFile.sav"];
NSDictionary *loadDict = [NSDictionary dictionaryWithContentsOfFile:filePath];
name = [loadDict valueForKey:#"name"];
last_episodue= [loadDict valueForKey:#"whereat"];
The variables name and last_episodue have been declared in the header file.
The program compiles and runs, however at runtime when trying to load the file, the LOAD part of the code executes, and when it finishes, the program stops working. This is the debugging information (first part):
2012-10-13 12:14:10.801 series[5223:303] -[NSISRestrictedToZeroMarkerVariable copyWithZone:]: unrecognized selector sent to instance 0x1001900c0
2012-10-13 12:14:10.803 series[5223:303] -[NSISRestrictedToZeroMarkerVariable copyWithZone:]: unrecognized selector sent to instance 0x1001900c0
2012-10-13 12:14:10.906 series[5223:303] (
Any idea what the problem might be? Thanks!
Edit: This is the content of the file where the saving takes place:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>name</key>
<array>
<string>a</string>
</array>
<key>whereat</key>
<array>
<string>a</string>
</array>
</dict>
</plist>
This is kind of a shot in the dark, since I can't tell without more context what your memory management looks like, but I just had this issue stemming from a variable whose value was not retained properly (we were assigning it using objc_setAssociatedObject but passing OBJC_ASSOCIATION_ASSIGN as the objc_AssociationPolicy). The pointer I held consistently ended up pointing over to an instance of NSISRestrictedToZeroMarkerVariable.
Since NSISRestrictedToZeroMarkerVariable is not a publicly-exposed class, what you're seeing is most likely the result of a memory overwrite. Set an exception breakpoint in Xcode and check out which line is throwing this error, and then track your memory management for that variable.

Problems extracting values from NSMutableArray

I'm trying to parse data from a plist file into a NSMutableArray.
In my plist
Root is a Dictionary containing an Array of 6 Numbers
I've created a label hooked with the IBOutlet UILabel *lbl4 object and I want this label to show the first element of the array made reading the plist. The problem is that the program crashes at the assigning instruction (the last one).
My code is this:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = [paths objectAtIndex:0];
NSString *plistPath = [docPath stringByAppendingPathComponent:#"settings.plist"];
if(![[NSFileManager defaultManager] fileExistsAtPath:plistPath]);
{
plistPath = [[NSBundle mainBundle] pathForResource:#"settings" ofType:#"plist"];
}
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
NSString *err = nil;
NSPropertyListFormat format;
NSDictionary *temp = (NSDictionary *) [NSPropertyListSerialization propertyListFromData:plistXML mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:&err];
if(!temp)
{
NSLog(#"Error reading plist: %#, format: %d", err, format);
}
self.dataSet = [NSMutableArray arrayWithArray:[temp objectForKey:#"Dadi"]];
[lbl4 setText:[NSString stringWithFormat:#"%#", [dataSet objectAtIndex:0]]];
The plist source code is the following:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Dadi</key>
<array/>
<key>D4</key>
<integer>0</integer>
<key>D6</key>
<integer>0</integer>
<key>D8</key>
<integer>0</integer>
<key>D10</key>
<integer>0</integer>
<key>D12</key>
<integer>0</integer>
<key>D20</key>
<integer>0</integer>
</dict>
</plist>
The Debug output says "2012-09-02 18:29:55.483 Faith[6014:707] * Terminating app due to uncaught exception 'NSRangeException', reason: '* -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'"
In your plist, the array stored at the dictionary's Dadi key is empty!
<key>Dadi</key>
<array/>
So
self.dataSet = [NSMutableArray arrayWithArray:[temp objectForKey:#"Dadi"]];
Sets self.dataSet to an empty array (i.e. even index:0 is beyond the bounds).
I would check for se.f.dataSet.count == 0 and provide a default in this case.
#warrenm mentioned in the comments that the structure of your plist is not what you might have expected. These are XML files, so any tag which ends with /> is "self-closing", and therefore always empty. To contain those numbers, you need to add an ending tag and place them inside:
<array>
<integer>7</integer>
</array>
Of course, on further evaluation, your existing plist has keys associated with those, so this is possibly also not the right solution. You'll need to evaluate what your needs are for that plist.

How to write an array to a file without html

The code I have right now looks like this:
NSString *path = #"/Users/student/Desktop/conf.txt"
NSArray *myArray = [NSArray arrayWithObjects:#"hello",#"world",#"etc",nil];
[myArray writeToFile:path atomically:YES];
When I look at the text file it writes it ends up looking like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<string>hello</string>
<string>world</string>
<string>etc</string>
</array>
</plist>
I don't want to display it as an html, I just want a simple text document with the 3 lines saying, "hello world etc".
Does anybody know how I could fix this?
Thanks
If your array contains strings and you wat to write them to file one per line you can first create string with all your words and then write it to file:
NSString *outString = [myArray componentsJoinedByString:#"\n"];
NSError *error = nil;
[outString writeToFile: path atomically:YES encoding:encoding:NSUTF8Encoding error:&error];
But if you want to serialize your array for future use plist format may be more convenient