Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSKeyedArchiver write XML (or other human readable)?

I have been trying to get the NSKeyedArchiver to write out my data in a human readable form (i.e. not as a binary file) I understand that I can use ...

setOutputFormat: NSPropertyListXMLFormat_v1_0

But ... I just can't seem to get the syntax right, can anyone point me in the right direction?

NSData *artistData;

NSLog(@"(*) - Save All");
artistData = [NSKeyedArchiver archivedDataWithRootObject:artistCollection ];
[artistData writeToFile:@"/Users/fgx/Desktop/stuff" atomically:YES];

EDIT

I added setOutputFormat (see below) but I get a warning at compile, what am I missing?

warning: NSData  may not respond to 'setOutputFormat:'

Here is the code I used.

NSLog(@"(*) - Save All");
artistData = [NSKeyedArchiver archivedDataWithRootObject:artistCollection ];
[artistData setOutputFormat: NSPropertyListXMLFormat_v1_0];
[artistData writeToFile:@"/Users/fgx/Desktop/stuff" atomically:YES];

gary

like image 719
fuzzygoat Avatar asked Oct 30 '09 17:10

fuzzygoat


3 Answers

This is what you have to do:

NSMutableData *data = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver setOutputFormat:NSPropertyListXMLFormat_v1_0];
[archiver encodeObject:artistCollection forKey:@"root"];
[archiver finishEncoding];
[data writeToFile:@"/Users/fgx/Desktop/stuff" atomically:YES];
[archiver release];

Note that you must use [archiver encodeObject:artistCollection forKey:@"root"] instead of [archiver encodeRootObject:artistCollection]; as suggested by Sean. This is because NSKeyedArchiver does not override NSCoder encodeRootObject: method. I consider this a bug and will report it to Apple.

Now, the real question is why do you want to write XML instead of the default binary format? If it's for debugging purpose, I suggest you to use TextWrangler which will allow you to transparently edit binary plist files as if they were XML.

like image 153
0xced Avatar answered Nov 15 '22 21:11

0xced


You need to first create an instance of NSKeyedArchiver instead of using the handy class method which will only return an NSData.

NSMutableData *data = [NSMutableData new];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];

// set the one property you specifically wanted
[archiver setOutputFormat:NSPropertyListXMLFormat_v1_0];

// now encode (and finalize) the actual object - note that encodeRootObject: is inherited
// from NSCoder. I assume this works :)
[archiver encodeRootObject:artistCollection];
[archiver finishEncoding];

// now finally write it out - the resulting encoded stuff should be waiting in the data object
[data writeToFile:@"/Users/fgx/Desktop/stuff" atomically:YES];

// cleanup after ourselves
[archiver release];
[data release];

Disclaimer: I wrote this code in the input box here and did not actually test it.

like image 27
Sean Avatar answered Nov 15 '22 20:11

Sean


'archivedDataWithRootObject:' is a class method, whereas 'setOutputFormat' is an instance method. From the documentation for 'archivedDataWithRootObject:':

An NSData object containing the encoded form of the object graph whose root object is rootObject. The format of the archive is NSPropertyListBinaryFormat_v1_0.

What you need to do is create an NSKeyedArchiver, and encode the root object. Also, make sure that all the contents of your collection implement 'encodeWithCoder' and 'initWithCoder'. See Encoding and Decoding Objects

The problem with this approach is you have to manually assign keys for each object you encode, which it doesn't sound like what you want, since you already have a collection. Both Arrays and Dictionaries contain methods to write to an xml file, which is a much easier approach.

[artistCollection writeToFile:@"/Users/fgx/Desktop/stuff" atomically:YES];

Discussion on this method (for both NSDictionary and NSArray):

If the receiver’s contents are all property list objects (NSString, NSData, NSArray, or NSDictionary objects), the file written by this method can be used to initialize a new array with the class method arrayWithContentsOfFile: or the instance method initWithContentsOfFile:. This method recursively validates that all the contained objects are property list objects before writing out the file, and returns NO if all the objects are not property list objects, since the resultant file would not be a valid property list. Blockquote

like image 22
bobDevil Avatar answered Nov 15 '22 22:11

bobDevil