views:

334

answers:

4

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

A: 
[artistData setOutputFormat: NSPropertyListXMLFormat_v1_0];
Azeem.Butt
`setOutputFormat:` is an NSKeyedArchiver method. Sending such a message to an NSData object isn't going to work.
Peter Hosey
so I should be looking at something like [NSKeyedArchiver setOutputFormat: NSPropertyListXMLFormat_v1_0]; I am not at my computer right now, I will have a better look tonight.
fuzzygoat
fuzzygoat: No, because `setOutputFormat:` is an instance method, so sending it to the NSKeyedArchiver class won't work, either. You must create an instance of NSKeyedArchiver and send the message to that.
Peter Hosey
+1  A: 

'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

bobDevil
The property list classes are all encodable, and you can encode a collection that contains non-property-list objects, whereas you can't make property list data from one.
Peter Hosey
+2  A: 

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.

Sean
Do not use `[archiver encodeRootObject:artistCollection];` Please read my answer to understand why.
0xced
+2  A: 

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.

0xced
Ah thats it, as both yourself and Sean point out I was using the class method rather that creating an instance, thats certainly where I was going wrong. Also your right, my primary reason was to help debug what was actually going into the file, viewing with BBEdit or TextWrangler is perfect for what I need. Much appreciated.
fuzzygoat