Plist Array to NSDictionary
Asked Answered
G

2

11

I have a plist:

<plist version="1.0">
  <array>
    <dict>
      <key>name</key>
      <string>Alabama</string>
      <key>abreviation</key>
      <string>AL</string>
      <key>date</key>
      <string>1819</string>
      <key>population</key>
      <string>4,627,851</string>
      <key>capital</key>
      <string>Montgomery</string>
      <key>largestCity</key>
      <string>Birmingham</string>
    </dict>
    <dict>
      <key>name</key>
      <string>Alaska</string>
      <key>abreviation</key>
      <string>AK</string>
      <key>date</key>
      <string>1959</string>
      <key>population</key>
      <string>683,478</string>
      <key>capital</key>
      <string>Juneau</string>
      <key>largestCity</key>
      <string>Anchorage</string>
    </dict>
    ...
  </array>
</plist>

I am trying to load it into an NSDictionary like this:

NSString *path = [[NSBundle mainBundle] pathForResource:@"stateInfo" ofType:@"plist"];

NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:path]) {
    NSLog(@"The file exists");
} else {
    NSLog(@"The file does not exist");
}

NSMutableDictionary *myDic = [[NSMutableDictionary alloc] initWithContentsOfFile:path];
//NSDictionary *myDic = [NSDictionary dictionaryWithContentsOfFile:path];
NSLog(@"The count: %i", [myDic count]);

NSArray *thisArray = [[NSArray alloc] initWithContentsOfFile:path];
NSLog(@"The array count: %i", [thisArray count]);

I always get an array count of 50 but a dictionary count of zero. So I tried looping through the array and adding it to the dictionary:

NSDictionary *eachState;
for (eachState in thisArray) {
    State *thisState = [[State alloc] initWithDictionary:eachState];
    [myDic setObject:thisState forKey:thisState.name];
}

But the loop is throwing an exception:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '*** -[NSCFDictionary setObject:forKey:]: mutating method sent to immutable object'

State is a class with properties matching my plist. What am I doing wrong? I see all kinds of related questions out here but I can't get it.

Get answered 27/8, 2009 at 18:53 Comment(2)
I got it. I had a mismatch and NsMutableDictionary was allocated as an NSDictionary.Get
And of course, I missed this comment until after I posted my answer.Arbour
A
17

The two problems:

  • Loading the plist into an NSDictionary:

This is a simple problem, which it seems you have already figured out. The global object in your plist is an array, not a dict, so when you load it into the dictionary, it doesn't know what to do (incompatable types), so you're getting an empty dictionary.

  • Looping through the array of dictionaries:

From the Exception you're getting, you are calling 'setObject:forKey:' on the dictionary, which is initialized as an NSDictionary, not an NSMutableDictionary. The pointer is typed as NSMutableDictionary, but not the actual in memory object. You need to change your line from.

NSMutableDictionary *myDic = [[NSDictionary alloc] initWithContentsOfFile:path];

to

NSMutableDictionary *myDic = [[NSMutableDictionary alloc] initWithContentsOfFile:path];

and actually, since loading the dictionary from the file gives you an empty dictionary, you are wasting cycles trying to load it from the file, and should just create a new one:

NSMutableDictionary *myDic = [[NSMutableDictionary alloc] init];
Arbour answered 27/8, 2009 at 19:18 Comment(1)
Great explanation! This will be of great help for future reference. thanksGet
P
10

A more flexible way to load plists into memory, that also allows you to create mutable plists:

NSData* data = [NSData dataWithContentsOfFile:path];
NSMutableArray* plist = [NSPropertyListSerialization propertyListFromData:data
                                                         mutabilityOption:NSPropertyListImmutable
                                                                   format:NSPropertyListXMLFormat_v1_0
                                                         errorDescription:NULL];

Thus your code could be implemented as:

NSString *path = [[NSBundle mainBundle] pathForResource:@"stateInfo" ofType:@"plist"];
NSData* data = [NSData dataWithContentsOfFile:path];
NSMutableArray* array = [NSPropertyListSerialization propertyListFromData:data
                                                         mutabilityOption:NSPropertyListImmutable
                                                                   format:NSPropertyListXMLFormat_v1_0
                                                         errorDescription:NULL];
if (array) {
  NSMutableDictionary* myDict = [NSMutableDictionary dictionaryWithCapacity:[array count]];
  for (NSDictionary* dict in array) {
    State* state = [[State alloc] initWithDictionary:dict];
    [myDict setObject:state forKey:state.name;
    [state release];
  }
  NSLog(@"The count: %i", [myDic count]);
} else {
  NSLog(@"Plist does not exist");
}
Pediform answered 28/8, 2009 at 21:28 Comment(1)
The format argument is an out argument, so the example isn't quite right here (and won't compile). Also, the result isn't necessarily an array. I supposed it could be, but the result could also be a dictionary, for example. Overall, I think this is a good addition to the answers given for the question.Flagwaving

© 2022 - 2024 — McMap. All rights reserved.