Created
June 26, 2017 13:47
-
-
Save hannesoid/10a35895e4dc5d6f1bb6428f7d4d23a5 to your computer and use it in GitHub Desktop.
Decodes an unknown JSON structure using Swift 4 Decoding
This file contains 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
import XCTest | |
// Requires a recent Swift 4 snapshot toolchain (Xcode 9 beta 2 not new enough) | |
public indirect enum JSONValue: Decodable { | |
case double(Double) | |
case string(String) | |
case bool(Bool) | |
case dictionary([String: JSONValue]) | |
case array([JSONValue]) | |
case `nil` | |
public init(from decoder: Decoder) throws { | |
let singleValueContainer = try decoder.singleValueContainer() | |
if let value = try? singleValueContainer.decode(Bool.self) { | |
self = .bool(value) | |
return | |
} else if let value = try? singleValueContainer.decode(String.self) { | |
self = .string(value) | |
return | |
} else if let value = try? singleValueContainer.decode(Double.self) { | |
self = .double(value) | |
return | |
} else if let value = try? singleValueContainer.decode([String: JSONValue].self) { | |
self = .dictionary(value) | |
return | |
} else if let value = try? singleValueContainer.decode([JSONValue].self) { | |
self = .array(value) | |
return | |
} else if singleValueContainer.decodeNil() { | |
self = .nil | |
return | |
} | |
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not find reasonable type to map to JSONValue")) | |
} | |
} | |
// MARK: - Convenience | |
extension JSONValue { | |
public var string: String? { | |
switch self { | |
case .string(let value): | |
return value | |
default: | |
return nil | |
} | |
} | |
public var double: Double? { | |
switch self { | |
case .double(let value): | |
return value | |
default: | |
return nil | |
} | |
} | |
public var bool: Bool? { | |
switch self { | |
case .bool(let value): | |
return value | |
default: | |
return nil | |
} | |
} | |
public var dictionary: [String: JSONValue]? { | |
switch self { | |
case .dictionary(let value): | |
return value | |
default: | |
return nil | |
} | |
} | |
public var array: [JSONValue]? { | |
switch self { | |
case .array(let value): | |
return value | |
default: | |
return nil | |
} | |
} | |
public var isNil: Bool { | |
switch self { | |
case .nil: | |
return true | |
default: | |
return false | |
} | |
} | |
} | |
class JSONValueTests: XCTestCase { | |
func testDecoding() { | |
let decoder = JSONDecoder() | |
let a = try! decoder.decode(JSONValue.self, from: """ | |
{ | |
"storage": { | |
"subthing": "stringValue", | |
"a": 2, | |
"bool": true, | |
"nilValue": null, | |
}, | |
"nilValue": null | |
} | |
""".data(using: .utf8)!) | |
XCTAssertEqual(a.dictionary?["storage"]?.dictionary?["subthing"]?.string, "stringValue") | |
XCTAssertEqual(a.dictionary?["storage"]?.dictionary?["a"]?.double, 2.0) | |
XCTAssertEqual(a.dictionary?["storage"]?.dictionary?["bool"]?.bool, true) | |
XCTAssertEqual(a.dictionary?["storage"]?.dictionary?["nilValue"]?.isNil, true) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment