RestKit请求值变换器?

时间:2014-04-10 14:22:54

标签: ios restkit restkit-0.20

我在使用RKValueTransformer将NSData图像字节序列化为请求的base64编码字符串时遇到了一些问题。在some help I received on stackoverflow之后,我能够对响应做反求。

这是我创建NSString到NSData值转换器的代码,它可以正常工作。我找到了null值转换器的索引并将其设置为afterNullTransformerIndex。我也将它设置为索引0,但后来我必须进行自己的空检查,这似乎没有问题。

    //add the base64 to NSData transformer after the null value transformer
RKBlockValueTransformer *base64StringToNSDataTransformer = [RKBlockValueTransformer valueTransformerWithValidationBlock:^BOOL(__unsafe_unretained Class inputValueClass, __unsafe_unretained Class outputValueClass) {
    return [inputValueClass isSubclassOfClass:[NSString class]] && [outputValueClass isSubclassOfClass:[NSData class]];
} transformationBlock:^BOOL(id inputValue, __autoreleasing id *outputValue, __unsafe_unretained Class outputClass, NSError *__autoreleasing *error) {
    RKValueTransformerTestInputValueIsKindOfClass(inputValue, [NSString class], error);
    RKValueTransformerTestOutputValueClassIsSubclassOfClass(outputClass, [NSData class], error);

    *outputValue = [[NSData alloc] initWithBase64EncodedString:(NSString *)inputValue options:NSDataBase64DecodingIgnoreUnknownCharacters];

    return YES;
}];
base64StringToNSDataTransformer.name = @"base64StringToNSDataTransformer";
[[RKValueTransformer defaultValueTransformer] insertValueTransformer:base64StringToNSDataTransformer atIndex:afterNullTransformerIndex];

这是我创建NSData到NSString值转换器的代码,它不起作用。我在transformationBlock:方法中设置了一个断点,但它永远不会被调用。:

    //add the NSData to String transformer for requests after the null value transformer
RKBlockValueTransformer *nsDataToBase64StringTransformer = [RKBlockValueTransformer valueTransformerWithValidationBlock:^BOOL(__unsafe_unretained Class inputValueClass, __unsafe_unretained Class outputValueClass) {
    return [inputValueClass isSubclassOfClass:[NSData class]] && [outputValueClass isSubclassOfClass:[NSString class]];
} transformationBlock:^BOOL(id inputValue, __autoreleasing id *outputValue, __unsafe_unretained Class outputClass, NSError *__autoreleasing *error) {
    RKValueTransformerTestInputValueIsKindOfClass(inputValue, [NSData class], error);
    RKValueTransformerTestOutputValueClassIsSubclassOfClass(outputClass, [NSString class], error);

    *outputValue = [((NSData *)inputValue) base64EncodedStringWithOptions:NSDataBase64Encoding76CharacterLineLength];

    return YES;
}];
nsDataToBase64StringTransformer.name = @"nsDataToBase64StringTransformer";
[[RKValueTransformer defaultValueTransformer] insertValueTransformer:nsDataToBase64StringTransformer atIndex:afterNullTransformerIndex];

就像我说的那样,我的断点永远不会在transformationBlock:方法中被调用,但valueTransformationWithValidationBlock:在序列化请求时会被调用一次,但只有在从Date转换为String时才会被调用。通过调试器和RestKit的代码查看堆栈,我在RKObjectParameterization.m中找到了这个方法:

- (void)mappingOperation:(RKMappingOperation *)operation didSetValue:(id)value forKeyPath:(NSString *)keyPath usingMapping:(RKAttributeMapping *)mapping
{
    id transformedValue = nil;
    if ([value isKindOfClass:[NSDate class]]) {
        [mapping.objectMapping.valueTransformer transformValue:value toValue:&transformedValue ofClass:[NSString class] error:nil];
    } else if ([value isKindOfClass:[NSDecimalNumber class]]) {
        // Precision numbers are serialized as strings to work around Javascript notation limits
        transformedValue = [(NSDecimalNumber *)value stringValue];
    } else if ([value isKindOfClass:[NSSet class]]) {
        // NSSets are not natively serializable, so let's just turn it into an NSArray
        transformedValue = [value allObjects];
    } else if ([value isKindOfClass:[NSOrderedSet class]]) {
        // NSOrderedSets are not natively serializable, so let's just turn it into an NSArray
        transformedValue = [value array];
    } else if (value == nil) {
        // Serialize nil values as null
        transformedValue = [NSNull null];
    } else {
        Class propertyClass = RKPropertyInspectorGetClassForPropertyAtKeyPathOfObject(mapping.sourceKeyPath, operation.sourceObject);
        if ([propertyClass isSubclassOfClass:NSClassFromString(@"__NSCFBoolean")] || [propertyClass isSubclassOfClass:NSClassFromString(@"NSCFBoolean")]) {
            transformedValue = @([value boolValue]);
        }
    }

    if (transformedValue) {
        RKLogDebug(@"Serialized %@ value at keyPath to %@ (%@)", NSStringFromClass([value class]), NSStringFromClass([transformedValue class]), value);
        [operation.destinationObject setValue:transformedValue forKeyPath:keyPath];
    }
}

当值为NSDate时,似乎RestKit正在使用值变换器!是否有一些我缺少的东西让值变换器处理请求?

编辑回答Wain的问题并提供更多详情

这是我的实体映射响应代码。记录实体拥有WTSImages的集合:

    RKEntityMapping *imageMapping = [RKEntityMapping mappingForEntityForName:@"WTSImage" inManagedObjectStore:self.managedObjectStore];
[imageMapping addAttributeMappingsFromDictionary:@{
                                                   @"id": @"dbId",
                                                   @"status": @"status",
                                                   @"type": @"type",
                                                   @"format": @"format",
                                                   @"width": @"width",
                                                   @"height": @"height",
                                                   @"image": @"imageData"
                                                   }];
imageMapping.identificationAttributes = @[@"dbId"];
[recordMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"images" toKeyPath:@"images" withMapping:imageMapping]];

WTSImage类是从CoreData生成的,如下所示:

@interface WTSImage : NSManagedObject

@property (nonatomic, retain) NSNumber * dbId;
@property (nonatomic, retain) NSString * format;
@property (nonatomic, retain) NSNumber * height;
@property (nonatomic, retain) NSData * imageData;
@property (nonatomic, retain) NSString * status;
@property (nonatomic, retain) NSString * type;
@property (nonatomic, retain) NSNumber * width;
@property (nonatomic, retain) WTSCaptureDevice *captureDevice;
@property (nonatomic, retain) WTSRecord *record;
@property (nonatomic, retain) WTSTempImageSet *tempImageSet;

@end

我创建了一个反向记录映射并添加了一个请求描述符。

RKEntityMapping *reverseRecordMapping = [recordMapping inverseMapping];
[self addRequestDescriptor:[RKRequestDescriptor requestDescriptorWithMapping:reverseRecordMapping objectClass:[WTSRecord class] rootKeyPath:@"records" method:RKRequestMethodAny]];

这是用于将我的图像对象映射到JSON的调试日志输出。 imageData元素看起来不像普通的base64编码字符串:

2014-04-10 11:02:39.537 Identify[945:60b] T restkit.object_mapping:RKMappingOperation.m:682 Mapped relationship object from keyPath 'images' to 'images'. Value: (
    {
    format = JPEG;
    height = 200;
    id = 0;
    image = <ffd8ffe0 00104a46 49460001 01000001 00010000 ffe10058 45786966 ... f77d7bf9 77b58fff d9>;
    status = C;
    type = MUGSHOT;
    width = 200;
})

这是POST,我的服务器拒绝了:

    2014-04-10 11:27:53.852 Identify[985:60b] T restkit.network:RKObjectRequestOperation.m:148 POST 'http://10.0.0.35:8080/Service/bs/records':
request.headers={
    Accept = "application/json";
    "Accept-Language" = "en;q=1, es;q=0.9, fr;q=0.8, de;q=0.7, ja;q=0.6, nl;q=0.5";
    "Content-Type" = "application/x-www-form-urlencoded; charset=utf-8";
    "User-Agent" = "Identify/1.0 (iPhone; iOS 7.1; Scale/2.00)";
}request.body=records[application]=Identify&records[createBy]=welcomed&records[createDt]=2014-04-10T15%3A27%3A42Z&records[description]&records[externalId]&records[groupId]=5&records[id]=0&records[images][][format]=JPEG&records[images][][height]=200&records[images][][id]=0&records[images][][image]=%3Cffd8ffe0%2000104a46%2049460001%2001000001%20000.......d773%20ffd9%3E&records[images][][status]=C&records[images][][type]=MUGSHOT&records[images][][width]=200&records[locked]&records[modifyBy]&records[modifyDt]&records[priv]

2 个答案:

答案 0 :(得分:4)

我有完全相同的问题 - 图像内容的NSData&lt; - &gt; NSString BASE64为Rest调用编码。我的出站工作很快就像你一样,但传入的映射有点棘手。

我提出了这个问题:https://github.com/RestKit/RestKit/issues/1949并通过一些解决问题的方法,发现你需要在RKPropertyMapping上设置propertyValueClass,以便让RestKit识别你想要将NSData转换为NSString。完成后,您将完成映射。

答案 1 :(得分:2)

  

在RKObjectMapping classForKeyPath:中,它无法找到我的'image'属性的类。似乎_objectClass是NSMutableDictionary而不是WTSImage。这导致该方法返回一个nil propertyClass

这是有道理的,因为请求的映射目标是NSMutableDictionary(并且源对象是WTSImage。因此,它不会应用任何特定的转换并落到{{1}你已经看到的不适应这种情况。

我认为使用变压器很难处理。

我现在能够想到的唯一方法就是向mappingOperation:didSetValue:forKeyPath:usingMapping:添加一个方法,比如WTSImage,它返回转换后的图像数据并在你的映射中使用它(这意味着你赢了)不能使用base64Image)。