Last active
September 2, 2021 22:35
-
-
Save klundberg/419de78d1b2f7a46c11b40b220ec5cd9 to your computer and use it in GitHub Desktop.
encode/decode arbitrary JSON data with swift 4's Codable
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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