有没有办法规范化NSJSONSerialization的json数据?

时间:2014-10-24 21:23:51

标签: ios objective-c xcode

似乎从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位模拟器)行为?

2 个答案:

答案 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;
}