Add support for dynamic nesting key serialization. closes #684

This commit is contained in:
Blake Watters
2013-01-03 23:02:49 -05:00
parent 896eef1a1b
commit 422768f6b1
4 changed files with 76 additions and 19 deletions

View File

@@ -418,7 +418,7 @@ static BOOL RKObjectContainsValueForKeyPaths(id representation, NSArray *keyPath
for (RKAttributeMapping *attributeMapping in attributeMappings) {
if ([self isCancelled]) return NO;
if ([attributeMapping.sourceKeyPath isEqualToString:RKObjectMappingNestingAttributeKeyName]) {
if ([attributeMapping.sourceKeyPath isEqualToString:RKObjectMappingNestingAttributeKeyName] || [attributeMapping.destinationKeyPath isEqualToString:RKObjectMappingNestingAttributeKeyName]) {
RKLogTrace(@"Skipping attribute mapping for special keyPath '%@'", attributeMapping.sourceKeyPath);
continue;
}
@@ -714,18 +714,31 @@ static BOOL RKObjectContainsValueForKeyPaths(id representation, NSArray *keyPath
- (void)applyNestedMappings
{
RKAttributeMapping *attributeMapping = [self.objectMapping attributeMappingForKeyOfRepresentation];
RKAttributeMapping *attributeMapping = [self.objectMapping mappingForSourceKeyPath:RKObjectMappingNestingAttributeKeyName];
if (attributeMapping) {
RKLogDebug(@"Found nested mapping definition to attribute '%@'", attributeMapping.destinationKeyPath);
id attributeValue = [[self.sourceObject allKeys] lastObject];
if (attributeValue) {
RKLogDebug(@"Found nesting value of '%@' for attribute '%@'", attributeValue, attributeMapping.destinationKeyPath);
_nestedAttributeSubstitution = [[NSDictionary alloc] initWithObjectsAndKeys:attributeValue, attributeMapping.destinationKeyPath, nil];
_nestedAttributeSubstitution = @{ attributeMapping.destinationKeyPath: attributeValue };
[self applyAttributeMapping:attributeMapping withValue:attributeValue];
} else {
RKLogWarning(@"Unable to find nesting value for attribute '%@'", attributeMapping.destinationKeyPath);
}
}
// Serialization
attributeMapping = [self.objectMapping mappingForDestinationKeyPath:RKObjectMappingNestingAttributeKeyName];
if (attributeMapping) {
RKLogDebug(@"Found nested mapping definition to attribute '%@'", attributeMapping.destinationKeyPath);
id attributeValue = [self.sourceObject valueForKeyPath:attributeMapping.sourceKeyPath];
if (attributeValue) {
RKLogDebug(@"Found nesting value of '%@' for attribute '%@'", attributeValue, attributeMapping.sourceKeyPath);
_nestedAttributeSubstitution = @{ attributeMapping.sourceKeyPath: attributeValue };
} else {
RKLogWarning(@"Unable to find nesting value for attribute '%@'", attributeMapping.destinationKeyPath);
}
}
}
- (void)cancel

View File

@@ -96,9 +96,9 @@
*/
+ (instancetype)requestMapping;
///---------------------------------
/// @name Managing Property Mappings
///---------------------------------
///----------------------------------
/// @name Accessing Property Mappings
///----------------------------------
/**
The aggregate collection of attribute and relationship mappings within this object mapping.
@@ -109,6 +109,7 @@
Returns the property mappings of the receiver in a dictionary, where the keys are the source key paths and the values are instances of `RKAttributeMapping` or `RKRelationshipMapping`.
@return The property mappings of the receiver in a dictionary, where the keys are the source key paths and the values are instances of `RKAttributeMapping` or `RKRelationshipMapping`.
@warning Note this method does not return any property mappings with a `nil` value for the source key path in the dictionary returned.
*/
@property (nonatomic, readonly) NSDictionary *propertyMappingsBySourceKeyPath;
@@ -116,6 +117,7 @@
Returns the property mappings of the receiver in a dictionary, where the keys are the destination key paths and the values are instances of `RKAttributeMapping` or `RKRelationshipMapping`.
@return The property mappings of the receiver in a dictionary, where the keys are the destination key paths and the values are instances of `RKAttributeMapping` or `RKRelationshipMapping`.
@warning Note this method does not return any property mappings with a `nil` value for the source key path in the dictionary returned.
*/
@property (nonatomic, readonly) NSDictionary *propertyMappingsByDestinationKeyPath;
@@ -129,6 +131,24 @@
*/
@property (nonatomic, readonly) NSArray *relationshipMappings;
/**
Returns the property mapping registered with the receiver with the given source key path.
@param sourceKeyPath The key path to retrieve.
*/
- (id)mappingForSourceKeyPath:(NSString *)sourceKeyPath;
/**
Returns the property mapping registered with the receiver with the given destinationKeyPath key path.
@param destinationKeyPath The key path to retrieve.
*/
- (id)mappingForDestinationKeyPath:(NSString *)destinationKeyPath;
///---------------------------
/// Managing Property Mappings
///---------------------------
/**
Adds a property mapping to the receiver.
@@ -226,14 +246,26 @@
- (void)addAttributeMappingFromKeyOfRepresentationToAttribute:(NSString *)attributeName;
/**
Returns the attribute mapping targeting the key of a nested dictionary in the source JSON.
Adds an attribute mapping to a dynamic nesting key from an attribute. The mapped attribute name can then be referenced wthin other attribute mappings to map content under the nesting key path.
For example, consider that we wish to map a local user object with the properties 'id', 'firstName' and 'email':
RKUser *user = [RKUser new];
user.firstName = @"blake";
user.userID = @(1234);
user.email = @"blake@restkit.org";
This attribute mapping corresponds to the attributeName configured via `mapKeyOfNestedDictionaryToAttribute:`
@return An attribute mapping for the key of a nested dictionary being mapped or nil
@see `addAttributeMappingFromKeyOfRepresentationToAttribute:`
And we wish to map it into JSON that looks like:
{ "blake": { "id": 1234, "email": "blake@restkit.org" } }
We can configure our request mapping to handle this like so:
RKObjectMapping *mapping = [RKObjectMapping requestMapping];
[mapping addAttributeMappingToKeyOfRepresentationFromAttribute:@"firstName"];
[mapping addAttributeMappingsFromDictionary:@{ @"(firstName).userID": @"id", @"(firstName).email": @"email" }];
*/
- (RKAttributeMapping *)attributeMappingForKeyOfRepresentation;
- (void)addAttributeMappingToKeyOfRepresentationFromAttribute:(NSString *)attributeName;
///----------------------------------
/// @name Configuring Mapping Options

View File

@@ -175,6 +175,7 @@ static RKSourceToDesinationKeyTransformationBlock defaultSourceToDestinationKeyT
{
NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:[self.propertyMappings count]];
for (RKPropertyMapping *propertyMapping in self.propertyMappings) {
if (! propertyMapping.sourceKeyPath) continue;
[dictionary setObject:propertyMapping forKey:propertyMapping.sourceKeyPath];
}
@@ -185,6 +186,7 @@ static RKSourceToDesinationKeyTransformationBlock defaultSourceToDestinationKeyT
{
NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:[self.propertyMappings count]];
for (RKPropertyMapping *propertyMapping in self.propertyMappings) {
if (! propertyMapping.destinationKeyPath) continue;
[dictionary setObject:propertyMapping forKey:propertyMapping.destinationKeyPath];
}
@@ -244,11 +246,6 @@ static RKSourceToDesinationKeyTransformationBlock defaultSourceToDestinationKeyT
NSStringFromClass([self class]), self, NSStringFromClass(self.objectClass), self.propertyMappings];
}
- (id)mappingForKeyPath:(NSString *)keyPath
{
return [self mappingForSourceKeyPath:keyPath];
}
- (id)mappingForSourceKeyPath:(NSString *)sourceKeyPath
{
for (RKPropertyMapping *mapping in self.propertyMappings) {
@@ -339,9 +336,9 @@ static RKSourceToDesinationKeyTransformationBlock defaultSourceToDestinationKeyT
[self addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:RKObjectMappingNestingAttributeKeyName toKeyPath:attributeName]];
}
- (RKAttributeMapping *)attributeMappingForKeyOfRepresentation
- (void)addAttributeMappingToKeyOfRepresentationFromAttribute:(NSString *)attributeName
{
return [self mappingForKeyPath:RKObjectMappingNestingAttributeKeyName];
[self addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:attributeName toKeyPath:RKObjectMappingNestingAttributeKeyName]];
}
- (RKAttributeMapping *)mappingForAttribute:(NSString *)attributeKey

View File

@@ -448,6 +448,21 @@
expect(string).to.equal(@"{\"name\":\"Blake Watters\",\"happy\":false}");
}
- (void)testSerializingWithDynamicNestingAttribute
{
NSDictionary *object = @{ @"name" : @"blake", @"occupation" : @"Hacker" };
RKObjectMapping *mapping = [RKObjectMapping requestMapping];
[mapping addAttributeMappingToKeyOfRepresentationFromAttribute:@"name"];
[mapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:@"name" toKeyPath:@"(name).name"]];
[mapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:@"occupation" toKeyPath:@"(name).job"]];
NSError *error = nil;
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:mapping objectClass:[NSDictionary class] rootKeyPath:nil];
NSDictionary *parameters = [RKObjectParameterization parametersWithObject:object requestDescriptor:requestDescriptor error:&error];
NSDictionary *expected = @{@"blake": @{@"name": @"blake", @"job": @"Hacker"}};
expect(parameters).to.equal(expected);
}
@end
#pragma mark - Dynamic Request Paramterization