緣起
我們在跟后端對接調試接口的時候,會將返回數(shù)據轉成字典后打印出來垢乙。這時候我們發(fā)現(xiàn),Xcode控制臺輸出的格式并非JSON格式酪刀,跟后端同事溝通協(xié)作的時候不是特別方便羊壹。特別要吐槽的是打印出來的中文是Unicode編碼齐婴,這也太反人性了。于是我寫了一個框架情妖,完美解決以上痛點诱担。
比如后端下發(fā)了這樣的JSON數(shù)據:
{
"address": "我是云南的,云南麗江的",
"info": {
"blog": "http://www.reibang.com/u/399cc7c53fad",
"isSingle": true,
"nickName": "小而白",
"score": 0.3
},
"name": "大魔王"
}
實際上蔫仙,我們是以NSData類型(Objective-C)去接收網絡數(shù)據的。上述JSON字符串對應的NSData恤煞,可通過如下方法轉換:
NSString *jsonStr = @"{\
\"address\": \"我是云南的施籍,云南麗江的\",\
\"info\": {\
\"blog\": \"http://www.reibang.com/u/399cc7c53fad\",\
\"isSingle\": true,\
\"nickName\": \"小而白\",\
\"score\": 0.3\
},\
\"name\": \"大魔王\"\
}";
NSData *jsonData = [jsonStr dataUsingEncoding:NSUTF8StringEncoding];
此時我們在本地模擬出了接收到的NSData數(shù)據,將jsonData轉化成字典:
NSError *serializationError = nil;
NSDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&serializationError];
//(AFNetWorking框架也是使用到這個方法)
debug調試responseObject信息截圖如下:
responseObject內存信息.png
直接手寫構造上面的字典:
NSDictionary *dict = @{
@"name":@"大魔王",
@"address":@"我是云南的喜喂,云南麗江的",
@"info": @{
@"nickName":@"小而白",
@"blog":@"http://www.reibang.com/u/399cc7c53fad",
@"score":@(0.3),
@"isSingle":@(YES)
}
};
NSLog(@"字典:%@",dict);
Xcode控制臺上打印顯示:
默認情況下打印字典.png
默認的打印格式除了中文亂碼以外玉吁,還有兩個不容忽視的問題:
1腻异、字典中@"isSingle":@(YES) BOOL類型的值被輸出為1
2、字典中@"score":@(0.3) 浮點數(shù)0.3被加上了引號變成了字符格式
解決方案
為了解決以上痛點捂掰,我查閱了相關資料曾沈,寫了一個輕量級的零侵入打印框架 SZJsonLog
使用該框架后打印效果是這樣的
SZJsonLog打印字典.png
完美還原原始JSON數(shù)據塞俱。我已經將該框架上傳到Github吏垮,您可以點擊 SZJsonLog源碼 下載,文件直接拖入工程膳汪,使用系統(tǒng)打印方法即可。祝您享用愉快粘我!
如何實現(xiàn)痹换?
其實很簡單,一句話就能說明白:依次取出字典中的鍵值對娇豫,進行字符串拼接。最終輸出JSON格式的字符串氮昧。
NSLog打印字典(NSDictionary)和數(shù)組(NSArray)的時候會走- (NSString *)descriptionWithLocale:(id)locale
來決定打印的字符串浦楣。所以現(xiàn)在我們在分類中重寫NSDictionary和NSArray(兩者可以相互嵌套)的- (NSString *)descriptionWithLocale:(id)locale
方法來獲得我們預期的結果。
在使用po命令調試的時候椒振,會走- (NSString *)debugDescription
方法,我們同樣覆蓋該方法來實現(xiàn)預期效果庐杨。
至于零侵入夹供,你們應該想到了,就是利用runtime的方法交換哮洽,在編譯時注冊經過改造的打印方法。
以NSDictionary示例氛什,貼出部分代碼
@implementation NSDictionary (SZJsonLog)
- (NSString *)szlog_descriptionWithLocale:(id)locale {
return [self descriptionWithLocale:locale indent:0];
}
- (NSString *)szlog_descriptionWithLocale:(id)locale indent:(NSUInteger)level {
NSMutableString *desc = [NSMutableString string];
NSMutableString *tabString = [[NSMutableString alloc] initWithCapacity:level];
for (NSUInteger i = 0; i < level; ++i) {
[tabString appendString:@"\t"];
}
NSString *tab = @"";
if (level > 0) {
tab = tabString;
}
[desc appendString:@"{\n"];
// 對字典排序
NSArray *allkeys = [self.allKeys sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
return [obj1 compare:obj2];
}];
for (id k in allkeys) {
id obj = [self objectForKey:k];
NSString *key = k;
if ([key isKindOfClass:[NSString class]]) {
key = [NSString stringWithFormat:@"\"%@\"", key];
}
if ([obj isKindOfClass:[NSString class]]) {
[desc appendFormat:@"%@\t%@: \"%@\",\n", tab, key, obj];
} else if ([NSStringFromClass([obj class]) isEqualToString:@"__NSCFBoolean"]) {
[desc appendFormat:@"%@\t%@: %s,\n", tab, key, [(NSNumber *)obj boolValue] ?"true": "false"];
} else if ([obj isKindOfClass:[NSArray class]]
|| [obj isKindOfClass:[NSDictionary class]]) {
[desc appendFormat:@"%@\t%@: %@,\n", tab, key, [obj descriptionWithLocale:locale indent:level + 1]];
} else if ([obj isKindOfClass:[NSData class]]) {
// 如果是NSData類型枪眉,嘗試去解析結果,以打印出可閱讀的數(shù)據
sz_convertToJsonString(obj, level, desc, tab, key);
} else if ([obj isKindOfClass:[NSNull class]]) {
[desc appendFormat:@"%@\t%@: null,\n", tab, key];
} else {
[desc appendFormat:@"%@\t%@: %@,\n", tab, key, obj];
}
}
// 查出最后一個,的范圍
NSRange range = [desc rangeOfString:@"," options:NSBackwardsSearch];
if (range.length) {
// 刪掉最后一個,
[desc deleteCharactersInRange:range];
}
[desc appendFormat:@"%@}", tab];
return desc;
}
- (NSString *)szlog_debugDescription {
return [self descriptionWithLocale:nil indent:0];
}
+ (void)load {
SZFUNCTIONSWAPREGISTER//方法交換
}
至于NSArray部分堡纬,無非字符串的拼接格式不同而已蒿秦,就不贅述。
Swift如何使用棍鳖?
很簡單,拖入工程后悴灵,只需將swift中的Dictionary轉換成NSDictionary來用即可骂蓖。舉個??
var dict: [String : Any] = [
"key1" : true,
"key2" : 0.3,
"key3" : ["key1" : ["1",2,"中文","http://www.baidu.com"],
"key2": "value2"],
]
print(dict as NSDictionary)
輸出截圖如下:
好的川尖,故事寫到這就結束了,謝謝您的拜讀叮喳!音響老師片尾曲請放起來。
慢畔濒!請留步锣咒,還有彩蛋,哈哈哈毅整。
或許你們有疑惑,開頭講到json字符串可以轉換成字典艇潭,調用系統(tǒng)現(xiàn)成方法就行。反過來蹋凝,字典轉成json字符串,難道就沒有相應的系統(tǒng)方法了房交?用得著大費周章這么解析拼接嗎伐割?的確是有的!我們這就試下
NSError *error = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:&error];//這里的dict仍是前面用到的字典用例
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSLog(@"系統(tǒng)默認打印格式打印jsonString:%@",jsonString);
控制臺輸出
字典轉json.png
有個很大的問題:0.3的精度丟失了白群。此外硬霍,網址加上了轉義的斜杠“\”。所以這個方案我是不能接受的唯卖。