Search code examples
iosobjective-crestkit

RestKit mapping with parent key as attribute and key contains parenthesis


Context:

// JSON
"Name_Field" : {
    "param_1":"value_1",
    "param_2":"value_2"
}

// Class
class Field {
    name
    param1
    param2
}

// Mapping Functionality
[mapping addAttributeMappingFromKeyOfRepresentationToAttribute:@"name"];
[mapping addAttributeMappingsFromDictionary:@{
    @"(name).param_1":@"param1",
    @"(name).param_2":@"param2"
}];

Problem:

I am currently working with the above JSON / Class / Mapping code. I have been using this for a while and everything has been working as expected.

Today I have run into the scenario where the key in the JSON contains parenthesis and causes the mapping to fail. Is there a way I can get this to work?

Thanks!

Example:

"Name (haha this will break)" : {
    "param_1":"value_1",
    "param_2":"value_2"
}

Solution

  • Solution:

    Update the RKStringByReplacingUnderscoresWithBraces method in RKPropertyMapping to not replace the parenthesis with braces and then be sure to use braces in your attribute mappings.

    // RKPropertyMapping
    static NSString *RKStringByReplacingUnderscoresWithBraces(NSString *string)
    {
        return string;
        //return [[string stringByReplacingOccurrencesOfString:@"(" withString:@"{"] stringByReplacingOccurrencesOfString:@")" withString:@"}"];
    }
    
    
    // Attribute Mappings
    [mapping addAttributeMappingsFromDictionary:@{
        @"{name}.param_1":@"param1",
        @"{name}.param_2":@"param2"
    }];
    

    Explanation:

    https://github.com/RestKit/RestKit/wiki/Object-mapping#handling-dynamic-nesting-attributes

    In the above RestKit documentation when you are attempting to map a key to property you are instructed to use addAttributeMappingFromKeyOfRepresentationToAttribute and then map the nested keys using parentheses to denote your property followed by a '.' and your nested key.

    In RestKit after it performs the map from the addAttributeMappingFromKeyOfRepresentationToAttribute attribute mapping it loops through all of your defined attribute mappings to replace your placeholder with the actual value from the key so the additional mapping operations can take place. During this process it creates a new RKPropertyMapping object and sets it's sourceKeyPath and destinationKeyPath. The property setters for these properties take the value and replace the parenthesis with braces and then RestKit does not find the incorrect mappings for the value with braces.

    Example Context:

    // JSON   
    { "blake": {
        "email": "blake@restkit.org",
        "favorite_animal": "Monkey"
        }
    }
    
    // Class
    @interface User : NSObject
    @property (nonatomic, copy) NSString* email
    @property (nonatomic, copy) NSString* username;
    @property (nonatomic, copy) NSString* favoriteAnimal;
    @end
    
    // Mapping
    RKObjectMapping* mapping = [RKObjectMapping mappingForClass:[User class] ];
    [mapping addAttributeMappingFromKeyOfRepresentationToAttribute:@"username"];
    [mapping addAttributeMappingsFromDictionary:@{
        @"(username).email": @"email",
        @"(username).favorite_animal": @"favoriteAnimal"
    }];
    

    Example Mapping:

    // Resulting Attribute Mappings
    @"{username}.email": @"email",
    @"{username}.favorite_animal": @"favoriteAnimal"
    
    
    // 1. The attribute mapping has been performed for 'blake' > 'username'
    // 2. RestKit now loops through your attribute mappings to replace '{username}' with 'blake' so your further nested attribute mappings can take place
    // 3. Example: @"{username}.email": @"email"
          sourceKeyPath = @"{username}.email"
          destinationKeyPath = @"email"
    
          * replaces values *
          sourceKeyPath = @"blake.email"
          destinationKeyPath = @"email"
    
          * creates a new RKPropertyMapping object *
          RKPropertyMapping
            - sourceKeyPath = @"blake.email"
            - destinationKeyPath = @"email"
    

    Example Mapping With Parenthesis:

    // JSON   
    { "blake (a.k.a GOAT)": {
        "email": "blake@restkit.org",
        "favorite_animal": "Monkey"
        }
    }
    
    // Resulting Attribute Mappings
    @"{username}.email": @"email",
    @"{username}.favorite_animal": @"favoriteAnimal"
    
    
    // 1. The attribute mapping has been performed for 'blake (a.k.a GOAT)' > 'username'
    // 2. RestKit now loops through your attribute mappings to replace '{username}' with 'blake (a.k.a GOAT)' so your further nested attribute mappings can take place
    // 3. Example: @"{username}.email": @"email"
          sourceKeyPath = @"{username}.email"
          destinationKeyPath = @"email"
    
          * replaces values *
          sourceKeyPath = @"blake (a.k.a GOAT).email"
          destinationKeyPath = @"email"
    
          * creates a new RKPropertyMapping object *
          RKPropertyMapping
            - sourceKeyPath = @"blake {a.k.a GOAT}.email" // DOES NOT MATCH
            - destinationKeyPath = @"email"