You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用JSONSerialization动态识别布尔值问题求助

问题根源

这个问题我之前也踩过坑!核心原因就是JSONSerialization的设计逻辑:它会把JSON中的布尔类型(true/false)映射成NSNumber实例,但NSNumber是所有数值类型的通用包装器——不管是JSON里的true1还是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

火山引擎 最新活动