jsonswiftnsjsonserialization

Using JSONSerialization() to dynamically figure out boolean values


I am getting a JSON string from the server (or file).

I want to parse that JSON string and dynamically figure out each of the value types.

However, when it comes to boolean values, JSONSerialization just converts the value to 0 or 1, and the code can't differentiate whether "0" is a Double, Int, or Bool.

I want to recognize whether the value is a Bool without explicitly knowing that a specific key corresponds to a Bool value. What am I doing wrong, or what could I do differently?

// What currently is happening:
let jsonString = "{\"boolean_key\" : true}"
let jsonData = jsonString.data(using: .utf8)!
let json = try! JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) as! [String:Any]

json["boolean_key"] is Double // true
json["boolean_key"] is Int // true
json["boolean_key"] is Bool // true

// What I would like to happen is below (the issue doesn't happen if I don't use JSONSerialization):
let customJson: [String:Any] = [
    "boolean_key" : true
]

customJson["boolean_key"] is Double // false
customJson["boolean_key"] is Int // false
customJson["boolean_key"] is Bool // true

Related:


Solution

  • When you use JSONSerialization, any Bool values (true or false) get converted to NSNumber instances which is why the use of is Double, is Int, and is Bool all return true since NSNumber can be converted to all of those types.

    You also get an NSNumber instance for actual numbers in the JSON.

    But the good news is that in reality, you actually get special internal subclasses of NSNumber. The boolean values actually give you __NSCFBoolean while actual numbers give you __NSCFNumber. Of course you don't actually want to check for those internal types.

    Here is a fuller example showing the above plus a workable solution to check for an actual boolean versus a "normal" number.

    let jsonString = "{\"boolean_key\" : true, \"int_key\" : 1}"
    let jsonData = jsonString.data(using: .utf8)!
    let json = try! JSONSerialization.jsonObject(with: jsonData, options: []) as! [String:Any]
    
    print(type(of: json["boolean_key"]!)) // __NSCFBoolean
    json["boolean_key"] is Double // true
    json["boolean_key"] is Int // true
    json["boolean_key"] is Bool // true
    
    print(type(of: json["int_key"]!)) // __NSCFNumber
    json["int_key"] is Double // true
    json["int_key"] is Int // true
    json["int_key"] is Bool // true
    
    print(type(of: json["boolean_key"]!) == type(of: NSNumber(value: true))) // true
    print(type(of: json["boolean_key"]!) == type(of: NSNumber(value: 1))) // false
    print(type(of: json["int_key"]!) == type(of: NSNumber(value: 0))) // true
    print(type(of: json["int_key"]!) == type(of: NSNumber(value: true))) // false