Serialization comes in two types: property list serialization and primitive data serialization. Examples of each are shown in the following sections
Serializing Property Lists
Serializing Primitive Data
Property lists are object graphs consisting exclusively of instances of NSArray, NSDictionary, NSString, NSData
, NSDate, NSNumber, and their mutable subclasses. The class methods of the NSPropertyListSerialization class handle the conversion between the NSData
byte stream and the object graph. The following code sample shows how to serialize a simple property list into an XML format.
NSData *dataRep; |
NSString *errorStr = nil; |
NSDictionary *propertyList; |
propertyList = [NSDictionary dictionaryWithObjectsAndKeys: |
@"Javier", @"FirstNameKey", |
@"Alegria", @"LastNameKey", nil]; |
dataRep = [NSPropertyListSerialization dataFromPropertyList: propertyList |
format: NSPropertyListXMLFormat_v1_0 |
errorDescription: &errorStr]; |
if (!dataRep) { |
// Handle error |
} |
The following code sample converts the XML data from above back into an object graph.
NSData *dataRep; // Assume this exists |
NSString *errorStr = nil; |
NSDictionary *propertyList; |
NSPropertyListFormat format; |
propertyList = [NSPropertyListSerialization propertyListFromData: dataRep |
mutabilityOption: NSPropertyListImmutable |
format: &format |
errorDescription: &errorStr]; |
if (!propertyList) { |
// Handle error |
} |
An object conforms to the NSObjCTypeSerializationCallBack protocol so that it can intervene in the serialization and deserialization process. The primary purpose of this protocol is to allow for the serialization of objects, which is not directly supported by Cocoa’s serialization facility.
Note: The serialization methods described in this section are obsolete and have been deprecated. Use NSPropertyListSerialization instead.
NSMutableData declares the method that is used to begin the serialization process:
- (void)serializeDataAt:(const void *)data |
ofObjCType:(const char *)type |
context:(id <NSObjCTypeSerializationCallBack>)callback |
This method can serialize all standard Objective-C types (int
, float
, character strings, and so on) except for objects, union
, and void *
. If, during the serialization process, an object is encountered, the callback object passed to the method is asked to provide the serialization.
Suppose the type being serialized is a structure of this description:
struct stockRecord { |
NSString *stockName; |
float value; |
}; |
The Objective-C type code for this structure is {@f}, so the serialization process begins with this code fragment:
MyHelperObject *helper; // assume exists and conforms to protocol |
NSMutableData *theData = [NSMutableData data]; |
struct stockRecord aRecord = {@"aCompany", 34.7}; |
[theData serializeDataAt:&aRecord ofObjCType:"{@f}" context:helper]; |
Because the first field of the structure is an unsupported type, the helper object is sent a serializeObjectAt:ofObjCType:intoData:
message, letting it serialize the object. helper
might implement the method in this way:
- (void)serializeObjectAt:(id *)objectPtr |
ofObjCType:(const char *)type |
intoData:(NSMutableData *)theData { |
NSString *nameObject; |
char *companyName; |
nameObject = *objectPtr; |
companyName = [nameObject UTF8String]; |
[theData serializeDataAt:&companyName |
ofObjCType:@encode(typeof(companyName)) |
context:nil]; |
} |
The callback object is free to serialize the target object as it wishes. In this case, helper
simply extracts the company name from the NSString object and then has that character string serialized. Once this callback method finishes executing, the original method (serializeDataAt:ofObjCType:context:
) resumes execution and serializes the second field of the structure. Since this second field contains a supported type (float
), the callback method is not invoked again.
The above implementation assumes the object sent to it is always an NSString object. If the helper object is to support serializing objects of different types (for example, if the serialized structure contains multiple objects of different types), you need to inspect objectPtr to identify what type of object is being serialized at each invocation. This can be done with the isKindOfClass:
method. For example, the implementation of serializeObjectAt:ofObjCType:intoData:
could be expanded to contain the following code fragment:
if ([*objectPtr isKindOfClass:[NSString class]]) { |
// Record the object type for deserialization |
char classType = ENCODE_STRING; |
[theData serializeDataAt:&classType |
ofObjCType:@encode(typeof(classType)) |
context:nil]; |
// Now encode the C string version of *objectPtr |
} |
else if (/* Test other types */) { |
// ... |
} |
Because the helper is serializing multiple object types, the object type being recorded must be stored within the byte stream so that the helper object can know what type of object to create when deserializing the byte stream. ENCODE_STRING
is assumed to be a macro that holds a numerical value identifying each object type the helper supports.
Deserialization follows a similar pattern, except in this case NSData
declares the central method that is used to begin the deserialization process:
- (void)deserializeDataAt:(void *)data |
ofObjCType:(const char *)type |
atCursor:(unsigned *)cursor |
context:(id <NSObjCTypeSerializationCallBack>)callback |
The deserialization of the example structure starts with a message to the NSData
object that contains the serialized data:
unsigned cursor = 0; |
[theData deserializeDataAt:&aRecord ofObjCType:"{@f}" cursor:&cursor |
context:helper]; |
The cursor argument identifies where within theData
to read the data. It is a initialized to zero to start deserializing at the beginning of the data. The cursor
argument is incremented by the length of the data processed.
When this method is invoked, the callback object receives a deserializeObjectAt:ofObjCType:fromData:atCursor:
message, as declared in this protocol. The callback object can then reestablish the first field of the structure. For example, helper
might implement the method in this way:
- (void) deserializeObjectAt:(id *)objectPtr |
ofObjCType:(const char *)type |
fromData:(NSData *)theData |
atCursor:(unsigned *)cursor { |
char *companyName; |
[theData deserializeDataAt:&companyName ofObjCType:"*" atCursor:cursor |
context:nil]; |
*objectPtr = [[NSString stringWithCString:companyName] retain]; |
} |
If helper
supports multiple object types, as described in “Serializing,” you need to read the type code first and create the appropriate object:
char classType; |
[theData deserializeDataAt:&classType ofObjCType:@encode(typeof(classType)) |
atCursor:cursor context:nil]; |
switch (classType) { |
case ENCODE_STRING: |
// Read the C string and create an NSString |
break; |
/* ... */ |
} |
© 2002, 2009 Apple Inc. All Rights Reserved. (Last updated: 2009-02-04)