Created
January 27, 2012 13:10
-
-
Save dhoerl/1688697 to your computer and use it in GitHub Desktop.
Converting a NSDictionary to a Binary PList
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
When I posted this on Apple’s Cocoadev listserver, I never did get pointers to code to convert a NSDictionary to a binary formatted plist, but did get pointers to look at NSPropertyListSerialization - something I had already (mis)read a few times. | |
The description for the dataFromPropertyList class method seems confusing if you don't understand that you can create a property list with no keys: that is, you can create one with a single compliant object, and the key becomes "root". | |
So, in the unlikely event that some else someday has the same mental block, here is actual tested code that takes a NSDictionary and creates a binary plist file: | |
NSDictionary *dict; | |
char *payload = "This is the payload"; | |
dict = [NSDictionary dictionaryWithObjectsAndKeys: | |
@"Hello world", @"greeting", | |
[NSData dataWithBytes:payload length:strlen(payload)], @"payload", | |
[NSNumber numberWithInt:10], @"result", | |
nil ]; | |
// get a text representation, for comparison | |
[dict writeToFile:@"/tmp/text.plist" atomically:NO]; | |
NSString *err = nil; | |
NSData *plist; | |
NSFileManager *manager; | |
BOOL ret; | |
manager = [NSFileManager defaultManager]; | |
plist = [NSPropertyListSerialization dataFromPropertyList:dict | |
format:NSPropertyListBinaryFormat_v1_0 | |
errorDescription:&err]; | |
if(plist == nil) { | |
NSLog(@"NSPropertyListSerialization error: %@", err); | |
return -1; | |
} | |
ret = [manager createFileAtPath:@"/tmp/binary.plist" contents:plist attributes:nil]; | |
if(ret == NO) { | |
NSLog(@"Create failed"); | |
return -1; | |
} | |
Just to prove to myself that yes, you can create a property list with other objects like NSNumbers or raw NSData, I added these two tests: | |
plist = [NSPropertyListSerialization dataFromPropertyList:[NSNumber numberWithInt:1] | |
format:NSPropertyListBinaryFormat_v1_0 | |
errorDescription:&err]; | |
if(plist == nil) { | |
NSLog(@"NSPropertyListSerialization error: %@", err); | |
return -1; | |
} | |
ret = [manager createFileAtPath:@"/tmp/tst1.plist" contents:plist attributes:nil]; | |
if(ret == NO) { | |
NSLog(@"Create failed"); | |
return -1; | |
} | |
plist = [NSPropertyListSerialization dataFromPropertyList:[NSData dataWithBytes:payload length:strlen(payload)] | |
format:NSPropertyListBinaryFormat_v1_0 | |
errorDescription:&err]; | |
if(plist == nil) { | |
NSLog(@"NSPropertyListSerialization error: %@", err); | |
return -1; | |
} | |
ret = [manager createFileAtPath:@"/tmp/tst2.plist" contents:plist attributes:nil]; | |
if(ret == NO) { | |
NSLog(@"Create failed"); | |
return -1; | |
} | |
Then, Ken Thomases (on Cocoadev) provided a shorter version, by having the NSData instance write the file. Jean-Daniel Dupas suggested using CFPropertyListWriteToStream to bypass the intermediate NSData object (a speed and memory footprint improvement). Here is the above code wrapped in a short command line test program: | |
int main (int argc, const char * argv[]) { | |
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; | |
NSString *err = nil; | |
NSData *plist; | |
NSDictionary *dict; | |
char *payload = "This is the payload"; | |
dict = [NSDictionary dictionaryWithObjectsAndKeys: | |
@"Hello world", @"greeting", | |
[NSData dataWithBytes:payload length:strlen(payload)], @"payload", | |
[NSNumber numberWithInt:10], @"result", | |
nil ]; | |
// 1 - get a XML (text) file representation | |
[dict writeToFile:@"/tmp/text.plist" atomically:NO]; | |
// get a binary file representation - creates an additional NSData in memory object | |
plist = [NSPropertyListSerialization dataFromPropertyList:dict | |
format:NSPropertyListBinaryFormat_v1_0 | |
errorDescription:&err]; | |
if(plist == nil) { | |
NSLog(@"NSPropertyListSerialization error: %@", err); | |
return -1; | |
} | |
// 2 - have the NSData object save a binary representation | |
[plist writeToFile:@"/tmp/binary1.plist" atomically:NO]; | |
// get a binary file representation - no additional memory footprint | |
// NOTE: documentation does not state it, but NSOutputStream creates the file if it does not yet exist | |
NSOutputStream *str = [NSOutputStream outputStreamToFileAtPath:@"/tmp/binary2.plist" append:NO]; | |
if(str == nil) { | |
NSLog(@"cannot create output stream"); | |
return -1; | |
} | |
// 3 - stream a binary representation | |
[str open]; | |
CFIndex idx = CFPropertyListWriteToStream(dict, (CFWriteStreamRef)str, kCFPropertyListBinaryFormat_v1_0, (CFStringRef *)&err); | |
if(idx == 0) { | |
NSLog(@"CFPropertyListWriteToStream error: %x %@", err, err); | |
return -1; | |
} | |
[str close]; | |
[pool drain]; | |
return 0; | |
} | |
Hope this is of use to someone else. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Originally posted in 2008 on my iWeb site (iWeb is going away soon).