Objective C Serialization

So I am under way with KPM Project 2 on the iPhone and we have decided to go down a Level Designer route for Box2D so we can produce levels easily! The level designer is being coded by my esteemed colleague Russ King (the K in KPM) and I am interpreting the XML on the iPhone to produce a level.

I have been google’ing about and cant seem to find a way to load the XML into an Object Structure in a way that I am happy with! Coming from a C# background I get this out of the box, being a newbie Objective C programmer I might have missed some easy way of doing it, if I have its too late now anyway because I coded one!

I thought this might be useful in case other people are struggling so I’ll post it up here (so far I have only needed to load from XML and the schema I use doesn’t use attributes but should be easily extendable).

First off follow the instructions here:

http://foobarpig.com/iphone/touchxml-installation-guide.html

to install TouchXML then add my XMLLoader class you your project and extend it with anything you want loading from XML. I was going to use Objective C’s form of reflection to automatically link Node to Property but I haven’t had time and this works for my purposes for now!

Here is the code! Please be nice I am new to Objective C :D also if this is useful let me know and I might make it a proper lib some day!

Usage:

Extend XMLLoader and Implement the initWithXML method in all classes that are being populated from XML in the usual way and use the methods available to populate the properties e.g. My favourite is the ClassArrayFromXPathOfType method as this accepts a collection of Nodes and populates it with instances of the Class you specify (the Class you specify must extend XMLLoader to work).

Example Load Code:

+(ClassType *) createClassFromXML:(NSString *)path
{
    NSData *xmlData = [NSData dataWithContentsOfFile:path];

    CXMLDocument *doc = [[[CXMLDocument alloc] initWithData:xmlData options:0 error:nil] autorelease];
    CXMLElement *xmlDoc;
    NSArray *nodes = NULL; 

    nodes = [doc nodesForXPath:@"/FirstNode" error:nil];

    if ([nodes count] > 0)
    {
        xmlDoc = (CXMLElement *)[nodes objectAtIndex:0];
    }
    else
    {
        [NSException raise:@"First Node Missing" format:@"XPath: /FirstNode"];
    }

    [nodes release];

    ClassType *classInstance = [[ClassType alloc] initWithXML:xmlDoc];

    [classInstance autorelease];

    return classInstance;
}

Example Constructor:

-(id) initWithXML:(CXMLElement *) xmlDocument
{
    if ((self = [super initWithXML:xmlDocument]))
    {
        _intProperty = [super intFromXPath:@"IntProperty"];
        _floatProperty = [super intFromXPath:@"FloatProperty"];
        _stringProperty = [super stringFromXPath:@"StringProperty"];
        _classProperty = [[ClassName alloc] initWithXML:[super nodeFromXPath:@"ClassName"]];
        _arrayProperty = [super classArrayFromXPath:@"CollectionNodes/*" OfType:@"ClassType"];
    }

    return self;
}

Header:

#import 

@class CXMLElement;

@interface XMLLoader : NSObject
{
    CXMLElement *_document;
}

-(id) initWithXML:(CXMLElement *) xmlDocument;
-(NSString *) stringFromXPath:(NSString *) xPath;
-(int) intFromXPath:(NSString *) xPath;
-(float) floatFromXPath:(NSString *) xPath;
-(BOOL) boolFromXPath:(NSString *) xPath;
-(CXMLElement *) nodeFromXPath:(NSString *) xPath;
-(NSArray *) nodesFromXPath:(NSString *) xPath;
-(NSArray *) classArrayFromXPath:(NSString *) xPath OfType:(NSString *) classType;

@end

Class:

#import "XMLLoader.h"
#import "TouchXML.h"

@implementation XMLLoader

-(id) initWithXML:(CXMLElement *) xmlDocument
{
    if ((self = [super init]))
    {
        _document = xmlDocument;
    }

    return self;
}

-(void) dealloc
{
    [_document release];

    [super dealloc];
}

-(NSString *) stringFromXPath:(NSString *) xPath
{
    NSString *value;
    NSArray *nodes = NULL;
    CXMLElement *element = nil; 

    nodes = [_document nodesForXPath:xPath error:nil];

    if ([nodes count] > 0)
    {
        element = (CXMLElement *)[nodes objectAtIndex:0];
        value = [element stringValue];
    }
    else
    {
        [NSException raise:@"XMLLoader: XPath not found!" format:@"XPath: %@", xPath];
    }

    [nodes release];

    return value;
}

-(int) intFromXPath:(NSString *) xPath
{
    NSString *stringValue = [self stringFromXPath:xPath];

    return [stringValue intValue];
}

-(float) floatFromXPath:(NSString *) xPath
{
    NSString *stringValue = [self stringFromXPath:xPath];

    return [stringValue floatValue];
}

-(BOOL) boolFromXPath:(NSString *) xPath
{
    NSString *stringValue = [self stringFromXPath:xPath];

    return [stringValue boolValue];
}

-(CXMLElement *) nodeFromXPath:(NSString *) xPath
{
    CXMLElement *value;
    NSArray *nodes = NULL; 

    nodes = [_document nodesForXPath:xPath error:nil];

    if ([nodes count] > 0)
    {
        value = (CXMLElement *)[nodes objectAtIndex:0];
    }
    else
    {
        [NSException raise:@"XMLLoader: XPath not found!" format:@"XPath: %@", xPath];
    }

    [nodes release];

    return value;
}

-(NSArray *) nodesFromXPath:(NSString *) xPath
{
    NSArray *nodes = NULL; 

    nodes = [_document nodesForXPath:xPath error:nil];

    return nodes;
}

-(NSArray *) classArrayFromXPath:(NSString *) xPath OfType:(NSString *) classType
{
    NSArray *returnValues;
    NSMutableArray *values = [[NSMutableArray alloc] init];
    NSArray *nodes = [self nodesFromXPath:xPath];

    for (CXMLElement *node in nodes)
    {
        XMLLoader *classInstance = [[NSClassFromString(classType) alloc] initWithXML:node];

        [values addObject:classInstance];

        [classInstance release];
    }

    returnValues = [NSArray arrayWithArray:values];

    [values release];

    return returnValues;
}

@end
About these ads

Tags: , , ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


Follow

Get every new post delivered to your Inbox.

%d bloggers like this: