在iOS 11以下,如果我们想要将字典数据转化成json格式,并且按key排序。可是Apple并没有给我们提供现有的API接口。
1 背景
在iOS 11以下,如果我们想要将字典数据转化成json格式,并且按key排序。可是Apple并没有给我们提供现有的API接口。
在iOS 11及以上的版本,直接调用API接口即可,如下所示方法,在options参数传入NSJSONWritingSortedKeys属性即可达到要求,它是按照字典的顺序对json的key进行排序。
+ (nullable NSData *)dataWithJSONObject:(id)obj options:(NSJSONWritingOptions)opt error:(NSError **)error;
所以,为了支持iOS 11以下的设备,我们自己实现了一个json的可以排序算法。
2 注意事项
对我们写的算法来说需要支持,以下几点。
- json有嵌套的关系,所以排序算法需要支持嵌套排序。
- Apple提供的算法只对key进行排序,如果存在数组类型,不对数组元素进行排序,保持原样输出,但是如果数组包含字典则需要对该字典递归排序。如下原json结构:
dic = @{@"b":@"v",
@"a":@"r",
@"w":@[@"2",@"1",@"0"],
@"key":@[@"2",@"1",
@{@"b":@"v",
@"a":@"r"
}
],
@"0a":@"sfsdf"
}
经过算法排序后如下。
dic = @{@"0a":@"sfsdf",
@"a":@"r",
@"b":@"v",
@"w":@[@"2",@"1",@"0"],// 不排序,原样输出
@"key":@[@"2",@"1",
@{@"a":@"r", // 字典结构,嵌套排序
@"b":@"v"
}
]
}
- 经本人测试,本算法支持的json排序结构如下:
1.多层嵌套的json
{
{{},{},...},
{},
....
}
2.数组和字典相互嵌套
{
[{},{},...],
[],
{
[],
[],
....
},
....
}
3 算法实现
不同于Apple官网给我们提供的升序排序,本算法还支持倒序排序。
# SortDictionaryByKeys.m文件
/// 按照字典的key排序,返回json的数据格式
/// @param dict 要转换成
/// @param asc @"YES" 代表升序,@"NO" 降序
+(NSString*)jsonStringWithDict:(NSDictionary*)dict ascend:(NSString *)asc{
NSArray*keys = [dict allKeys];
if (keys.count==0) {
return nil;
}
int flag=0;// 在拼接json的时候判断是不是字典来判断是不要双引号
NSArray*sortedArray;
NSString*str =@"{\"";// 拼接json的转换的结果
// 自定义比较器来比较key的ASCII码
sortedArray = [keys sortedArrayUsingComparator:^NSComparisonResult(id obj1,id obj2) {
return[obj1 compare:obj2 options:NSNumericSearch];//升序排序
}];
// 逐个取出key和value,然后拼接json
for (int i=0; i<sortedArray.count; i++) {
NSString *categoryId;
if ([asc isEqualToString:@"YES"]) {// 升序排序
categoryId = sortedArray[i];
}else{ // 降序排序
categoryId = sortedArray[sortedArray.count-1-i];
}
id value = [dict objectForKey:categoryId];
if([value isKindOfClass:[NSDictionary class]]) {
flag=1;
value = [SortDictionaryByKeysUtils jsonStringWithDict:value ascend:asc];
}
// 拼接json串的分割符
if([str length] !=2) {
str = [str stringByAppendingString:@",\""];
}
// 对数组类型展开处理
if([value isKindOfClass:[NSArray class]]){
str = [str stringByAppendingFormat:@"%@\":[",categoryId];
str = [SortDictionaryByKeysUtils sortInner:value jsonString:str];
// 因为在 处理完数组类型后,json已经拼接好,直接拼接下一个串
continue;
}
if (flag==1) {
str = [str stringByAppendingFormat:@"%@\":%@",categoryId,value];
flag=0;
}else{
if(![value isKindOfClass:[NSString class]]){// 如果是number类型,value不需要加双引号
// 如果是BOOl类型则转化为false和true
Class c = [value class];
NSString * s = [NSString stringWithFormat:@"%@", c];
if([s isEqualToString:@"__NSCFBoolean"]){
if ([value isEqualToNumber:@YES]) {
str = [str stringByAppendingFormat:@"%@\":%@",categoryId,@"true"];
}else{
str = [str stringByAppendingFormat:@"%@\":%@",categoryId,@"false"];
}
}else{
str = [str stringByAppendingFormat:@"%@\":%@",categoryId,value];
}
}else{
str = [str stringByAppendingFormat:@"%@\":\"%@\"",categoryId,value];
}
}
}
str = [str stringByAppendingString:@"}"];
NSLog(@"result json = %@", str);
return str;
}
+(NSString *) sortInner:(NSArray *) array jsonString:(NSString *)json{
NSString *string =@"";
NSInteger location = 0;
for (int i=0; i< array.count; i++) {
if(i!=0&&i< array.count) {
json = [json stringByAppendingString:@","];
}
id arr = [array objectAtIndex:i];
if([arr isKindOfClass:[NSDictionary class]]){// 如果数组里包含字典,则对该字典递归排序
location = i;
string=[SortDictionaryByKeysUtils jsonStringWithDict:arr ascend:@"YES"];
json = [json stringByAppendingFormat:@"%@",string];
}else{
if([arr isKindOfClass:[NSString class]]){
json = [json stringByAppendingFormat:@"\"%@\"",arr];
}else{
// 如果是BOOl类型则转化为false和true
Class c = [arr class];
NSString * s = [NSString stringWithFormat:@"%@", c];
if([s isEqualToString:@"__NSCFBoolean"]){
if ([arr isEqualToNumber:@YES]) {
json = [json stringByAppendingFormat:@"%@",@"true"];
}else{
json = [json stringByAppendingFormat:@"%@",@"false"];
}
}else{
json = [json stringByAppendingFormat:@"%@",arr];
}
}
}
}
json = [json stringByAppendingString:@"]"];
return json;
}
4 测试
NSDictionary *dic6 =@{@"b":@"v",@"a":@"r",@"w":@[@"2",@"1",@"0"],@"key":@[@"2",@"1",@{@"b":@"v",@"a":@"r"}],@"0a":@"sfsdf"};
if( [[self __dictionaryToJson:dic] isEqualToString:[SortDictionaryByKeys jsonStringWithDict:dic ascend:@"YES"]]){
NSLog(@"%@",@"解析正确");
}
// 工程中具体的使用
- (NSString *) __dictionaryToJson:(NSDictionary *)dic{
NSError *error = nil;
NSData *jsonData ;
NSString *jsonString;
if (@available(iOS 11.0, *)) {
jsonData = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingSortedKeys error:&error];
jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
} else {
jsonString = [SortDictionaryByKeys jsonStringWithDict:dic ascend:@"YES"];
return jsonString;
}
总结
该算法的核心是没一层json结构都是类似的,并且先对json的key排序,然后再取key对应的value拼接成json串,所以对整个排序来说,各个key:value的顺序是不变的。所以要递归处理每一层即可。
由于递归,绝大部分可以用栈的方式处理,所以本算法还可以以这样的思路实现。采用栈的思想:
如果遇到非}或者非],则采取入栈操作,如果遇到}和],进行出栈,直到匹配{或者[,停止出栈。然后对出栈取到的{}或者[]里的key进行排序,然后在拼接。
本算法没有采取这种思路是因为,不容易分割字符串,在处理出栈的字符串有点麻烦。特别是逗号不好处理。提供一个思路是最开始使用一个不会出现在json数据里的字符串来代替逗号,然后在处理完后在换回来。