< Previous PageNext Page > Hide TOC

Serializing Objects

Serialization comes in two types: property list serialization and primitive data serialization. Examples of each are shown in the following sections

Contents:

Serializing Property Lists
Serializing Primitive Data


Serializing Property Lists

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
}

Serializing Primitive Data

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.

Serializing

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.

Deserializing

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;
    /* ... */
}


< Previous PageNext Page > Hide TOC


© 2002, 2009 Apple Inc. All Rights Reserved. (Last updated: 2009-02-04)


Did this document help you?
Yes: Tell us what works for you.
It’s good, but: Report typos, inaccuracies, and so forth.
It wasn’t helpful: Tell us what would have helped.