使用JSONSerialization动态识别布尔值问题求助
这个问题我之前也踩过坑!核心原因就是JSONSerialization的设计逻辑:它会把JSON中的布尔类型(true/false)映射成NSNumber实例,但NSNumber是所有数值类型的通用包装器——不管是JSON里的true、1还是1.0,最终都会变成NSNumber对象。这就导致你拿到这个对象时,完全没法区分它原本是布尔值还是数字。
不需要预先指定布尔值的键也能准确识别类型,这里有几个靠谱的方案:
方案1:用第三方JSON库(最省心)
像Swift里的SwiftyJSON或者Objective-C里的JSONKit这类成熟的库,在解析时会主动保留每个JSON元素的原始类型信息,你可以直接查询值的类型,完全不用提前知道哪些键是布尔值。
举个SwiftyJSON的例子:
import SwiftyJSON let jsonString = """ {"isActive": true, "count": 1, "score": 1.0} """ if let data = jsonString.data(using: .utf8) { let json = JSON(data: data) for (key, value) in json { switch value.type { case .bool: print("\(key) 是布尔值:\(value.boolValue)") case .int: print("\(key) 是整数:\(value.intValue)") case .double: print("\(key) 是浮点数:\(value.doubleValue)") // 其他类型(字符串、数组、对象等)的处理 default: break } } }
运行这段代码,你会发现isActive会被准确识别为布尔类型,不会和count(整数)、score(浮点数)混淆。
方案2:自定义JSON类型枚举(无第三方依赖)
如果你不想引入第三方库,可以自己实现一个能表示所有JSON类型的枚举,让它遵循Codable协议,这样用系统的JSONDecoder解析时,就能准确区分每个值的原始类型。
先定义枚举:
enum JSONValue: Codable { case string(String) case int(Int) case double(Double) case bool(Bool) case array([JSONValue]) case object([String: JSONValue]) case null init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() // 按优先级尝试解码不同类型 if let string = try? container.decode(String.self) { self = .string(string) } else if let int = try? container.decode(Int.self) { self = .int(int) } else if let double = try? container.decode(Double.self) { self = .double(double) } else if let bool = try? container.decode(Bool.self) { self = .bool(bool) } else if let array = try? container.decode([JSONValue].self) { self = .array(array) } else if let object = try? container.decode([String: JSONValue].self) { self = .object(object) } else if container.decodeNil() { self = .null } else { throw DecodingError.dataCorruptedError(in: container, debugDescription: "不支持的JSON类型") } } func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch self { case .string(let string): try container.encode(string) case .int(let int): try container.encode(int) case .double(let double): try container.encode(double) case .bool(let bool): try container.encode(bool) case .array(let array): try container.encode(array) case .object(let object): try container.encode(object) case .null: try container.encodeNil() } } }
然后用它解析JSON:
let jsonString = """ {"isActive": true, "count": 1, "score": 1.0} """ if let data = jsonString.data(using: .utf8) { do { let jsonValue = try JSONDecoder().decode(JSONValue.self, from: data) if case .object(let jsonObject) = jsonValue { for (key, value) in jsonObject { switch value { case .bool(let bool): print("\(key): 布尔值(\(bool))") case .int(let int): print("\(key): 整数(\(int))") case .double(let double): print("\(key): 浮点数(\(double))") // 其他类型的处理逻辑 default: break } } } } catch { print("解析失败:\(error)") } }
这个方案完全基于系统API,不需要任何第三方依赖,而且能完美区分所有原始JSON类型,也不需要预先指定任何键。
方案3:结合原始JSON字符串做二次验证(不推荐)
如果你实在不想用上面两种方法,还有一种笨办法:解析完JSONSerialization的结果后,针对每个NSNumber,回到原始的JSON字符串中找到对应的位置,检查它的原始文本是true/false还是数字。
不过这种方法非常繁琐,尤其是处理嵌套的JSON结构时,需要写递归逻辑来跟踪每个元素的路径,而且效率很低,所以只建议在极端场景下使用。
内容的提问来源于stack exchange,提问作者kgaidis




