RestKit请求值变换器?

Dan*_*Dan 5 ios restkit restkit-0.20

我在使用RKValueTransformer将NSData图像字节序列化为请求的base64编码字符串时遇到一些问题.在我通过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];
Run Code Online (Sandbox Code Playgroud)

这是我创建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];
Run Code Online (Sandbox Code Playgroud)

就像我说的那样,我的断点永远不会在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];
    }
}
Run Code Online (Sandbox Code Playgroud)

只有当值是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]];
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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

RKEntityMapping *reverseRecordMapping = [recordMapping inverseMapping];
[self addRequestDescriptor:[RKRequestDescriptor requestDescriptorWithMapping:reverseRecordMapping objectClass:[WTSRecord class] rootKeyPath:@"records" method:RKRequestMethodAny]];
Run Code Online (Sandbox Code Playgroud)

这是用于将我的图像对象映射到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;
})
Run Code Online (Sandbox Code Playgroud)

这是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]
Run Code Online (Sandbox Code Playgroud)

小智 5

我有完全相同的问题 - 图像内容的 NSData <-> NSString BASE64 为 Rest 调用编码。我和你一样快速地让出站工作,但传入的映射有点棘手。

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


Wai*_*ain 2

在 RKObjectMapping classForKeyPath: 中,无法找到我的“image”属性的类。看来 _objectClass 是 NSMutableDictionary 而不是 WTSImage。这导致该方法返回 nil propertyClass

这是有道理的,因为请求的映射目标是NSMutableDictionary( 并且源对象是WTSImage。因此,它不应用任何特定的转换,并且mappingOperation:didSetValue:forKeyPath:usingMapping:您已经看到的失败不适合这种情况。

我认为使用变压器很难解决这个问题。

我现在能想到处理它的唯一方法是添加一个方法WTSImage,比如说base64Image返回转换后的图像数据并在您的映射中使用它(这意味着您将无法使用[recordMapping inverseMapping])。