Skip to content

Instantly share code, notes, and snippets.

@ApoorvKhatreja
Last active December 22, 2022 19:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ApoorvKhatreja/5b7fba5689db7958652fd3d3fc7b5109 to your computer and use it in GitHub Desktop.
Save ApoorvKhatreja/5b7fba5689db7958652fd3d3fc7b5109 to your computer and use it in GitHub Desktop.
A Swift enum that represents a valid JSON value, that is equatable and supports implicit type conversion. Instead of parsing to [String: Any], parse to [String: JSONValue], and enjoy type safety.
enum JSONValue: Equatable,
ExpressibleByStringLiteral,
ExpressibleByFloatLiteral,
ExpressibleByIntegerLiteral,
ExpressibleByDictionaryLiteral,
ExpressibleByArrayLiteral,
ExpressibleByBooleanLiteral,
ExpressibleByNilLiteral,
RawRepresentable {
typealias RawValue = Any
case string(String)
case number(Float)
case object([String: JSONValue])
case array([JSONValue])
case boolean(Bool)
case null(NSNull)
init(stringLiteral value: String) {
self = .string(value)
}
init(floatLiteral value: Float) {
self = .number(value)
}
init(integerLiteral value: Int) {
self = .number(Float(value))
}
init(dictionaryLiteral elements: (String, JSONValue)...) {
self = .object(Dictionary(uniqueKeysWithValues: elements))
}
init(arrayLiteral elements: JSONValue...) {
self = .array(elements)
}
init(booleanLiteral value: Bool) {
self = .boolean(value)
}
init(nilLiteral: ()) {
self = .null(NSNull())
}
init?(rawValue: RawValue) {
switch rawValue {
case let string as String:
self = .string(string)
case let number as Float:
self = .number(number)
case let number as Int:
self = .number(Float(number))
case let object as [String: JSONValue]:
self = .object(object)
case let array as [JSONValue]:
self = .array(array)
case let boolean as Bool:
self = .boolean(boolean)
case let null as NSNull:
self = .null(null)
default:
return nil
}
}
var rawValue: Any {
switch self {
case .string(let string):
return string
case .number(let number):
return number
case .object(let object):
return rawValue(for: object)
case .array(let array):
return rawValue(for: array)
case .boolean(let boolean):
return boolean
case .null(let null):
return null
}
}
private func rawValue(for object: [String: JSONValue]) -> [String: Any] {
var terminalRawValue: [String: Any] = [:]
for (key, jsonValue) in object {
terminalRawValue[key] = jsonValue.rawValue
}
return terminalRawValue
}
private func rawValue(for array: [JSONValue]) -> [Any] {
var terminalRawValue: [Any] = []
for jsonValue in array {
terminalRawValue.append(jsonValue.rawValue)
}
return terminalRawValue
}
}
@ApoorvKhatreja
Copy link
Author

Sample usage:

var jsonObject: [String: JSONValue] {
        [
            "string_key": "string_value",
            "float_key": 20.0,
            "int_key": 1,
            "object_key": [
                "nested_key": "string_value"
            ],
            "array_key": [
                "string_value",
                1,
                false
            ],
            "bool_key": true,
            "null_key": nil
        ]
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment