iOS JSON解析换行符(\r、\n、\r\n)

项目需要对JSON返回的换行符进行解析,系统提供了解析JSON的方法[NSJSONSerialization JSONObjectWithData:options:error:]
AFNetworking 内部也是采用该方法来解析服务器返回的内容。所以,我只需要 hook 该系统方法,当解析失败的时候进行处理就行了,这样就可以一劳永逸,也无需修改其他地方的代码。

服务器采用的是 Windows + C# 组合,通过后台添加的数据回车符为:\r\n,通过 Android 应用提交的回车符则是:\n,而 iOS 平台则是:\r,我统一将回车符替换成 \r ,进行转义后再次尝试解析JSON。

#import <objc/message.h>


@interface NSJSONSerialization (SpecialCharacters)

@end


@implementation NSJSONSerialization (SpecialCharacters)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Method originalMethod = class_getClassMethod(self, @selector(JSONObjectWithData:options:error:));
        Method swizlledMethod = class_getClassMethod(self, @selector(sc_JSONObjectWithData:options:error:));
        method_exchangeImplementations(originalMethod, swizlledMethod);
    });
}

+ (id)sc_JSONObjectWithData:(NSData *)data options:(NSJSONReadingOptions)opt error:(NSError * _Nullable __autoreleasing *)error {
    if (!data.length) return nil;
    NSError *serializationError = nil;
    id responseObject = [self sc_JSONObjectWithData:data options:opt error:&serializationError];
    if (!responseObject) {
        if (error) {
            *error = serializationError;
        }

        if (serializationError && serializationError.code == 3840) {
            NSString *serializationString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            if (!serializationString) {
                return nil;
            }
            serializationString = [serializationString stringByReplacingOccurrencesOfString:@"(\\r\\n|\\r|\\n)" withString:@"\\\\r" options:NSRegularExpressionSearch range:NSMakeRange(0, serializationString.length)];

            NSData *serializationData = [serializationString dataUsingEncoding:NSUTF8StringEncoding];
            responseObject = [self sc_JSONObjectWithData:serializationData options:opt error:nil];
#ifndef DEBUG
            if (responseObject && error) {
                *error = nil;
            }
#endif
        }
    }

    return responseObject;
}

@end