JSONSerialization turns Bool value to NSNumber

I have a Codable object that I need to convert to Dictionary so I first encode it to convert it to Data and then use JSONSerialization to convert that Data to Any and then use as? [String: Any] to get the dictionary.

The conversion is successful but due to use of JSONSerialisation, Bool types are being converted to NSNumber but I want to retain the Bool value inside the Dictionary

import Foundation

struct Employee: Codable {
    let employeeID: Int?
    let meta: Meta?
}

struct Meta: Codable {
    let demo: Bool?
}

let employeeObject = Employee(employeeID: 1, meta: Meta(demo: true))

do {
    let encodedObject = try JSONEncoder().encode(employeeObject)
    
    let dictionary = try JSONSerialization.jsonObject(with: encodedObject, options: .fragmentsAllowed) as? [String: Any]
    
    print(dictionary ?? [:])
    
} catch  {
    print(error)
}

OUTPUT

["meta": {
demo = 1; }, "employeeID": 1]

demo property is being converted to NSNumber but I want to retain the Bool value

Well, that is how JSONSerialization works — but this scenario is a hack anyways.
You could try to do the translation with reflection (Mirror) instead.

Well, that is how JSONSerialization works

Right. Due to its Objective-C heritage, it can’t return JSON Boolean values as Swift Bool values because those are value types, not objects.

What you’ll find, however, is that it does use CFBoolean for JSON Booleans, so you can detect whether the original was a Boolean or a number by testing against kCFBooleanTrue and kCFBooleanFalse. Consider:

// One of these 1’s is not like other…

let m = dictionary!["meta"]! as! [String:Any]
let d = m["demo"]! as! NSNumber
print(d)                        // 1
print(d === kCFBooleanTrue)     // true
print(d === kCFBooleanFalse)    // false

let e = dictionary!["employeeID"]! as! NSNumber
print(e)                        // 1
print(e === kCFBooleanTrue)     // false
print(e === kCFBooleanFalse)    // false

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

2 Likes

Non-hacky approach would be to write an Encoder (or find a library that has it) that encodes directly into a Dictionary instead of going through json

This cocoa pod seems to do what you want DictionaryDecoder on CocoaPods.org