Skip to content

Instantly share code, notes, and snippets.

@klundberg
Last active September 2, 2021 22:35
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save klundberg/419de78d1b2f7a46c11b40b220ec5cd9 to your computer and use it in GitHub Desktop.
Save klundberg/419de78d1b2f7a46c11b40b220ec5cd9 to your computer and use it in GitHub Desktop.
encode/decode arbitrary JSON data with swift 4's Codable
enum JSONValue: Codable, Equatable {
struct JSONValueDecodingError: Error {
let message: String
}
case boolean(Bool)
case number(Double)
case string(String)
case array([JSONValue?])
case object([String: JSONValue?])
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let boolean = try? container.decode(Bool.self) {
self = .boolean(boolean)
} else if let number = try? container.decode(Double.self) {
// todo: try decoding all number types in standard library somehow?
self = .number(number)
} else if let string = try? container.decode(String.self) {
self = .string(string)
} else if let array = try? container.decode([JSONValue?].self) {
self = .array(array)
} else if let dictionary = try? container.decode([String: JSONValue?].self) {
self = .object(dictionary)
} else {
throw JSONValueDecodingError(message: "could not decode a valid JSONValue")
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .boolean(let value):
try container.encode(value)
case .number(let value):
try container.encode(value)
case .string(let value):
try container.encode(value)
case .array(let values):
try container.encode(values)
case .object(let valueDictionary):
try container.encode(valueDictionary)
}
}
}
extension JSONValue: ExpressibleByBooleanLiteral {
init(booleanLiteral value: Bool) {
self = .boolean(value)
}
}
extension JSONValue: ExpressibleByIntegerLiteral {
init(integerLiteral value: Int) {
self = .number(Double(value))
}
}
extension JSONValue: ExpressibleByFloatLiteral {
init(floatLiteral value: Double) {
self = .number(value)
}
}
extension JSONValue: ExpressibleByStringLiteral {
init(stringLiteral value: String) {
self = .string(value)
}
}
extension JSONValue: ExpressibleByArrayLiteral {
init(arrayLiteral elements: JSONValue?...) {
self = .array(elements)
}
}
extension JSONValue: ExpressibleByDictionaryLiteral {
init(dictionaryLiteral elements: (String, JSONValue?)...) {
self = .object(Dictionary.init(uniqueKeysWithValues: elements))
}
}
let json1: JSONValue = ["Hi"]
do {
let data = try JSONEncoder().encode(json1)
let json2 = try JSONDecoder().decode(JSONValue.self, from: data)
print(json1 == json2)
} catch {
print(error)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment