我在使用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]
答案 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
)。