我们在跟后端对接调试接口的时候,往往需将返回数据转成字典(NSDictionary)后打印出来。这时候我们发现,Xcode控制台输出的格式并非JSON格式,跟后端同事沟通协作的时候不是特别方便。特别要吐槽的是打印出来的中文是Unicode编码,这也太反人性了。稍后这篇文章就为您提供一个优雅的解决方案。
比如后端下发了这样的JSON数据:
"address"
:
"我是云南的,云南丽江的"
,
"info"
:
{
"blog"
:
"https://www.jianshu.com/u/399cc7c53fad"
,
"isSingle"
:
true
,
"nickName"
:
"小而白"
,
"score"
:
0.3
"name"
:
"大魔王"
实际上,我们是以NSData类型(Objective-C)去接收网络数据的。上述JSON字符串对应的NSData,可通过如下方法转换:
NSString *jsonStr = @"{\
\"address\": \"我是云南的,云南丽江的\",\
\"info\": {\
\"blog\": \"https://www.jianshu.com/u/399cc7c53fad\",\
\"isSingle\": true,\
\"nickName\": \"小而白\",\
\"score\": 0.3\
\"name\": \"大魔王\"\
NSData *jsonData = [jsonStr dataUsingEncoding:NSUTF8StringEncoding];
此时我们在本地模拟出了接收到的NSData数据,将jsonData转化成字典:
NSError *serializationError = nil;
NSDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&serializationError];
debug调试responseObject信息截图如下:
我们用字面量的形式将该字典直观表征出来:
NSDictionary *dict = @{
@"name":@"大魔王",
@"address":@"我是云南的,云南丽江的",
@"info": @{
@"nickName":@"小而白",
@"blog":@"https://www.jianshu.com/u/399cc7c53fad",
@"score":@(0.3),
@"isSingle":@(YES)
NSLog(@"字典:%@",dict);
Xcode控制台上打印显示:
默认的打印格式除了中文乱码
以外,还有两个不容忽视的问题:
字典中@"isSingle":@(YES)
BOOL类型的值被输出为1
字典中@"score":@(0.3)
浮点数0.3被加上了引号变成了字符格式
为了解决以上痛点,我查阅了相关资料,却没有找到一个十分满意的现成方案。于是一个轻量级的零侵入打印框架SZJsonLog应运而生。
使用SZJsonLog后打印效果是这样的
完美还原原始JSON数据。我已经将该框架上传到Github,您可以点击 SZJsonLog源码 下载,将里面的“SZJsonLog”文件直接拖入工程,使用系统原生打印方法即可。该框架仅在debug模式下编译与生效,所以不占用正式包的存储空间。祝您享用愉快!
框架原理?
其实很简单,一句话就能说明白:依次取出字典中的键值对/数组中的元素,进行JSON格式字符串拼接。
NSLog打印字典(NSDictionary)或数组(NSArray)的时候会走- (NSString *)descriptionWithLocale:(id)locale
来决定打印的字符串。所以现在我们在分类中重写NSDictionary和NSArray(两者可以相互嵌套)的- (NSString *)descriptionWithLocale:(id)locale
方法来获得我们预期的结果。
在使用po命令调试的时候,会走- (NSString *)debugDescription
方法,我们同样覆盖该方法来实现预期效果。
至于零侵入,你们应该想到了,就是利用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]]) {
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来用即可。举个🌰
let dict: [String : Any?] = [
"key1" : true,
"key2" : 0.3,
"key3" : ["key1" : ["1",2,"中文","http://www.baidu.com"],
"key2": "value2"],
"key4" : nil
print(dict as NSDictionary)
输出截图如下:
但swift
项目中lldb po
命令打印json格式失效。打印效果如下:
好的,故事写到这就结束了,谢谢您的拜读!音响老师片尾曲请放起来。
慢!请留步,还有彩蛋,哈哈哈。
或许你们有疑惑,开头讲到json字符串可以转换成字典,调用系统现成方法就行。反过来,字典转成json字符串,难道就没有相应的系统方法了?用得着大费周章这么解析拼接吗?的确是有的!我注意到网上某同类框架也是使用的这个方式。我们这就简单试下
NSError *error = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:&error];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSLog(@"系统默认打印格式打印jsonString:%@",jsonString);
控制台输出
输出格式是json格式,但有个很大的问题:0.3的精度丢失了。此外,网址加上了转义的斜杠“\”。所以这个方案我是不能接受的。
到这里这篇文章就真的结束了。再次把框架下载链接奉上SZJsonLog源码
【iOS】让NSLog打印字典显示得更好看(解决中文乱码并显示成JSON格式)