import Foundation private struct InitializationError: Error {} extension JSON { /// Create a JSON value from anything. /// /// Argument has to be a valid JSON structure: A `Double`, `Int`, `String`, /// `Bool`, an `Array` of those types or a `Dictionary` of those types. /// /// You can also pass `nil` or `NSNull`, both will be treated as `.null`. public init(_ value: Any) throws { switch value { case _ as NSNull: self = .null case let opt as Optional where opt == nil: self = .null case let num as NSNumber: if num.isBool { self = .bool(num.boolValue) } else { self = .number(num.doubleValue) } case let str as String: self = .string(str) case let bool as Bool: self = .bool(bool) case let array as [Any]: self = .array(try array.map(JSON.init)) case let dict as [String:Any]: self = .object(try dict.mapValues(JSON.init)) default: throw InitializationError() } } } extension JSON { /// Create a JSON value from an `Encodable`. This will give you access to the “raw” /// encoded JSON value the `Encodable` is serialized into. public init(encodable: T) throws { let encoded = try JSONEncoder().encode(encodable) self = try JSONDecoder().decode(JSON.self, from: encoded) } } extension JSON: ExpressibleByBooleanLiteral { public init(booleanLiteral value: Bool) { self = .bool(value) } } extension JSON: ExpressibleByNilLiteral { public init(nilLiteral: ()) { self = .null } } extension JSON: ExpressibleByArrayLiteral { public init(arrayLiteral elements: JSON...) { self = .array(elements) } } extension JSON: ExpressibleByDictionaryLiteral { public init(dictionaryLiteral elements: (String, JSON)...) { var object: [String:JSON] = [:] for (k, v) in elements { object[k] = v } self = .object(object) } } extension JSON: ExpressibleByFloatLiteral { public init(floatLiteral value: Double) { self = .number(value) } } extension JSON: ExpressibleByIntegerLiteral { public init(integerLiteral value: Int) { self = .number(Double(value)) } } extension JSON: ExpressibleByStringLiteral { public init(stringLiteral value: String) { self = .string(value) } } // MARK: - NSNumber extension NSNumber { /// Boolean value indicating whether this `NSNumber` wraps a boolean. /// /// For example, when using `NSJSONSerialization` Bool values are converted into `NSNumber` instances. /// /// - seealso: https://stackoverflow.com/a/49641315/3589408 fileprivate var isBool: Bool { let objCType = String(cString: self.objCType) if (self.compare(trueNumber) == .orderedSame && objCType == trueObjCType) || (self.compare(falseNumber) == .orderedSame && objCType == falseObjCType) { return true } else { return false } } } private let trueNumber = NSNumber(value: true) private let falseNumber = NSNumber(value: false) private let trueObjCType = String(cString: trueNumber.objCType) private let falseObjCType = String(cString: falseNumber.objCType)