Last active
November 5, 2019 12:56
-
-
Save loromits/89e9aa76f820295bea1e18649e645ef6 to your computer and use it in GitHub Desktop.
Modification of https://gist.github.com/itaiferber/d26cf93355f3415f4f684510f21ca202 to be able to explore contents of JSON.
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 Foundation | |
public enum JSON : Codable { | |
case null | |
case boolean(Bool) | |
case number(Double) | |
case string(String) | |
case array([JSON]) | |
case dictionary([String : JSON]) | |
public init(from decoder: Decoder) throws { | |
let container = try decoder.singleValueContainer() | |
if container.decodeNil() { | |
self = .null | |
} else if let bool = try container.decodeIfMatched(Bool.self) { | |
self = .boolean(bool) | |
} else if let double = try container.decodeIfMatched(Double.self) { | |
self = .number(double) | |
} else if let string = try container.decodeIfMatched(String.self) { | |
self = .string(string) | |
} else if let array = try container.decodeIfMatched([JSON].self) { | |
self = .array(array) | |
} else if let dictionary = try container.decodeIfMatched([String : JSON].self) { | |
self = .dictionary(dictionary) | |
} else { | |
let context = DecodingError.Context(codingPath: decoder.codingPath, | |
debugDescription: "The type is incompatible with JSON.") | |
throw DecodingError.typeMismatch(JSON.self, context) | |
} | |
} | |
public func encode(to encoder: Encoder) throws { | |
var container = encoder.singleValueContainer() | |
switch self { | |
case .null: try container.encodeNil() | |
case .boolean(let boolean): try container.encode(boolean) | |
case .number(let number): try container.encode(number) | |
case .string(let string): try container.encode(string) | |
case .array(let array): try container.encode(array) | |
case .dictionary(let dictionary): try container.encode(dictionary) | |
} | |
} | |
var isNull: Bool { | |
if case .null = self { | |
return true | |
} | |
return false | |
} | |
var number: Double? { | |
if case .number(let value) = self { | |
return value | |
} | |
return nil | |
} | |
var boolean: Bool? { | |
if case .boolean(let value) = self { | |
return value | |
} | |
return nil | |
} | |
var string: String? { | |
if case .string(let value) = self { | |
return value | |
} | |
return nil | |
} | |
var array: [JSON]? { | |
if case .array(let value) = self { | |
return value | |
} | |
return nil | |
} | |
var dictionary: [String : JSON]? { | |
if case .dictionary(let value) = self { | |
return value | |
} | |
return nil | |
} | |
subscript(key: String) -> JSON? { | |
return dictionary?[key] | |
} | |
subscript(index: Int) -> JSON? { | |
return array.flatMap { index < $0.endIndex ? $0[index] : nil } | |
} | |
var value: Any? { | |
switch self { | |
case .null: return nil | |
case .boolean(let boolean): return boolean | |
case .number(let number): return number | |
case .string(let string): return string | |
case .array(let array): return array.compactMap { $0.value } | |
case .dictionary(let dictionary): return dictionary.compactMapValues { $0.value } | |
} | |
} | |
} | |
private extension SingleValueDecodingContainer { | |
func decodeIfMatched<T : Decodable>(_ type: T.Type) throws -> T? { | |
do { | |
return try self.decode(T.self) | |
} catch DecodingError.typeMismatch { | |
return nil | |
} | |
} | |
} |
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
extension JSON { | |
var playgroundDescription: String { | |
return String(describing: self) | |
.replacingOccurrences(of: "__lldb_expr_\\d+\\.", with: "", options: .regularExpression, range: nil) | |
.replacingOccurrences(of: "\"", with: "") | |
} | |
} | |
let json = """ | |
{ | |
"firstName": "John", | |
"lastName": "Smith", | |
"isAlive": true, | |
"age": 27, | |
"address": { | |
"streetAddress": "21 2nd Street", | |
"city": "New York", | |
"state": "NY", | |
"postalCode": "10021-3100" | |
}, | |
"phoneNumbers": [ | |
{ | |
"type": "home", | |
"number": "212 555-1234" | |
}, | |
{ | |
"type": "office", | |
"number": "646 555-4567" | |
}, | |
{ | |
"type": "mobile", | |
"number": "123 456-7890" | |
} | |
], | |
"children": [], | |
"spouse": null | |
} | |
""".data(using: .utf8)! | |
let decoded = try JSONDecoder().decode(JSON.self, from: json) | |
print(decoded["phoneNumbers"]?[1]?["number"]?.string) // Optional("646 555-4567") | |
print(decoded["phoneNumbers"]?.playgroundDescription) // Optional("array([JSON.dictionary([type: JSON.string(home), number: JSON.string(212 555-1234)]), JSON.dictionary([type: JSON.string(office), number: JSON.string(646 555-4567)]), JSON.dictionary([type: JSON.string(mobile), number: JSON.string(123 456-7890)])])") | |
print(decoded["isAlive"]?.boolean) // Optional(true) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment