似乎从XCode 6.1开始,iPhone 5S,iPhone 6和iPhone 6+模拟器(全部64位)都以不同的方式返回来自以下系统方法的数据(键的顺序不同),而不是32位模拟器对应物(例如iPhone 5模拟器)
+ (NSData *)dataWithJSONObject:(id)obj options:(NSJSONWritingOptions)opt error:(NSError **)error;
密钥排序的这种差异给我带来了问题,因为我们计算了该JSON数据的SHA1(转换为NSString *)并将其用于验证测试。由于订单发生了变化,SHA1发生了变化,验证失败了。
获取SHA1的简化示例代码(非ARC)如下:
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:dict
options:0
error:&error];
NSString * json = [[[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding] autorelease];
NSString * sha1 = [MyUtils computeSHA1:json];
+(NSString*) computeSHA1:(NSString*)input
{
const char *cstr = [input cStringUsingEncoding:NSUTF8StringEncoding];
NSData *data = [NSData dataWithBytes:cstr length:input.length];
NSNumber* dataLen = [NSNumber numberWithUnsignedInteger:data.length];
uint8_t digest[CC_SHA1_DIGEST_LENGTH];
CC_SHA1(data.bytes, dataLen.unsignedIntValue, digest);
NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
for(int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++)
[output appendFormat:@"%02x", digest[i]];
return output;
}
显然,这个键排序差异并不会发生在实际设备上(之前的行为已被保留)。
我也尝试使用NSJSONWritingPrettyPrinted
选项,但JSON排序在模拟器之间仍然不一致。
所以,问题是:是否有人建议如何规范化此类JSON数据,以免不受密钥排序变化的影响?或者,有没有办法获得以前的(32位模拟器)行为?
答案 0 :(得分:0)
不保证词典中的键排序。如果需要对它们进行排序,请将它们放入数组中并对它们进行排序。
答案 1 :(得分:-2)
下面的代码(非ARC)让我更好地规范化JSON输出。该代码假定下面的类方法都在一个名为MyUtils的类中。
只需将NSDictionary传递给序列化为“canonicalized JSON”到canonicalJSONRepresentationWithDictionary:
返回的NSString *然后包含序列化的JSON,其按键以字典方式/字母顺序以非人类可读的格式排序。
+(NSString *) canonicalJSONRepresentationWithDictionary:(NSDictionary *)dict
{
NSMutableString* json = [NSMutableString string];
[json appendString:@"{"];
NSArray* keys = [[dict allKeys] sortedArrayUsingComparator:^NSComparisonResult(NSString* a, NSString* b) {
return [a compare:b];
}];
for (int i = 0; i < keys.count; i++) {
NSString* key = keys[i];
[json appendFormat:@"\"%@\":", key];
if ([dict[key] isKindOfClass:[NSString class]]) {
[json appendFormat:@"\"%@\"", [MyUtils canonicalJSONRepresentationWithString:dict[key]]];
} else if ([dict[key] isKindOfClass:[NSDictionary class]]) {
[json appendString:[MyUtils canonicalJSONRepresentationWithDictionary:dict[key]]];
} else if ([dict[key] isKindOfClass:[NSArray class]]) {
[json appendString:[MyUtils canonicalJSONRepresentationWithArray:dict[key]]];
} else {
return nil;
}
if (i < keys.count - 1) {
[json appendString:@","];
}
}
[json appendString:@"}"];
return json;
}
+(NSString *) canonicalJSONRepresentationWithArray:(NSArray *) array
{
NSMutableString* json = [NSMutableString string];
[json appendString:@"["];
for (int i = 0; i < array.count; i++) {
if ([array[i] isKindOfClass:[NSString class]]) {
[json appendFormat:@"\"%@\"", [MyUtils canonicalJSONRepresentationWithString:array[i]]];
} else if ([array[i] isKindOfClass:[NSDictionary class]]) {
[json appendString:[MyUtils canonicalJSONRepresentationWithDictionary:array[i]]];
} else if ([array[i] isKindOfClass:[NSArray class]]) {
[json appendString:[MyUtils canonicalJSONRepresentationWithArray:array[i]]];
} else {
return nil;
}
if (i < array.count - 1) {
[json appendString:@","];
}
}
[json appendString:@"]"];
return json;
}
+(NSString *) canonicalJSONRepresentationWithString:(NSString *) string;
{
NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:string, @"a", nil];
NSError * error;
NSData * jsonData = nil;
NSString * json = nil;
jsonData = [NSJSONSerialization dataWithJSONObject:dict
options:0
error:&error];
if (!jsonData) {
NSLog(@"Got an error serializing json: %@", error);
return nil;
} else {
json = [[[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding] autorelease];
}
NSRange colonQuote = [json rangeOfString:@":\""];
NSRange lastQuote = [json rangeOfString:@"\"" options:NSBackwardsSearch];
NSRange range = NSMakeRange(colonQuote.location + 2, lastQuote.location - colonQuote.location - 2);
NSString* rc = [json substringWithRange:range];
return rc;
}