0

I'm trying to parse a SOAP response using RestKit. I'm able to successfully convert the SOAP XML response into objects if I define the mappings and relationships myself. But, I was wondering if it's possible to use introspection (or something else) to automatically convert the SOAP XML response into an Objective-C object.

Sample XML:

<return>
    <userId xsi:type="xsd:int">1113050187</userId>
    <location xsi:type="ns1:Location">
       <city xsi:type="xsd:string">San Francisco</city>
       <state xsi:type="xsd:string">California</state>
    </location>
</return>

I would like to convert this XML into this object:

@interface Return : NSObject

@property (strong, nonatomic) NSInteger userId; // attribute
@property (strong, nonatomic) Location *location; // relation

@end

The closest thing I could come up with is using introspection on the Return class to get all properties and using something like this for each attribute: [mapping addAttributeMapping:[RKObjectAttributeMapping mappingFromKeyPath:@"userId.text" toKeyPath:@"userId"]];

And for relations, I could again use introspection to find out all my class objects and use mapRelationship:withMapping: on each one

pshah
  • 2,052
  • 1
  • 21
  • 40
  • Why don't you use Sudzc? The plumbing code is automatically generated and you just need to make request and deal with it. It will create all the related objects too – TeaCupApp Jun 27 '12 at 02:15
  • I've tried Sudzc and had little luck with the generated code for my wsdl. I hear it works for a lot of people, but in my case it didn't! – pshah Jun 27 '12 at 03:13
  • why it didn't? any specific error? are using third party wsdl? or your own? – TeaCupApp Jun 27 '12 at 05:00
  • It is "RestKit" as in "RESTful toolkit". I don't know how compatible it will be with a SOAP interface (but I'm not an expert). Why don't you use Sudzc like @Owl says - it is what it is for. – Paul de Lange Jun 27 '12 at 07:11
  • I couldn't even get the generated code with ARC to compile. Without ARC, a lot of my calls wont work as expected and found it to buggy and had lots of memory leaks. I'm using my own wsdl generated by the Zend Soap server. Like I said before, I've heard good things about Sudzc, but unfortunately it didn't work for my project. – pshah Jun 27 '12 at 17:28

1 Answers1

0

I ended up defining a method that recursively maps the properties to XML nodes if their names match. The naming convention and the data type of the property is important for this to work.

I've tried my best to clean this up before I post it here, but let me know if you need any help.

- (RKObjectMapping *)mapMe:(Class)class
{
    RKObjectManager *objectManager = [RKObjectManager sharedManager];
    RKObjectMapping *mapping = [RKObjectMapping mappingForClass:class];
    id classType = objc_getClass([NSStringFromClass(class) UTF8String]);
    unsigned int outCount, i;
    objc_property_t *properties = class_copyPropertyList(classType, &outCount);
    for (i = 0; i < outCount; i++) {
        objc_property_t property = properties[i];
//        fprintf(stdout, "%s\n", property_getName(property));

        const char *type = property_getAttributes(property);
        NSString *typeString = [NSString stringWithUTF8String:type];
        NSArray *attributes = [typeString componentsSeparatedByString:@","];
//        NSLog(@"attributes = %@", attributes);
        NSString *typeAttribute = [attributes objectAtIndex:0];
//        NSLog(@"typeAttribute = %@", typeAttribute);
        NSString *propertyType = [typeAttribute substringFromIndex:1];

        if ([propertyType hasPrefix:@"@"] && [propertyType length] > 1) {
            NSString * typeClassName = [propertyType substringWithRange:NSMakeRange(2, [propertyType length]-3)];  //turns @"NSDate" into NSDate
            Class typeClass = NSClassFromString(typeClassName);
            if (typeClass != nil && ![typeClassName hasPrefix:@"NS"]) {
//            my custom class detected.
                RKObjectMapping *subMapping = [self mapMe:typeClass forObjectManager:objectManager];
                [mapping mapRelationship:[NSString stringWithUTF8String:property_getName(property)] withMapping:subMapping];
                [objectManager.mappingProvider setMapping:subMapping forKeyPath:[NSString stringWithUTF8String:property_getName(property)]];
            } else {
                [mapping addAttributeMapping:[RKObjectAttributeMapping mappingFromKeyPath:[NSString stringWithFormat:@"%@.text", [NSString stringWithUTF8String:property_getName(property)]] toKeyPath:[NSString stringWithUTF8String:property_getName(property)]]];
            }
        }
    }
    free(properties);
    return mapping;
}

And then to map it automatically call it as:

[self mapMe:[Myclass class]];
pshah
  • 2,052
  • 1
  • 21
  • 40