Last active
June 19, 2023 15:03
-
-
Save tail-call/94ca29a100d9462f855faa739d9b86ef to your computer and use it in GitHub Desktop.
I want to encode/decode [String: Any] that badly...
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
// | |
// JSONWrapper.swift | |
// | |
// Created by Maria Zaitseva on 16.03.2021. | |
// | |
import Foundation | |
/// This class provides a straightforward way to encode/decode | |
/// objects of type `[String: Any]` to and from JSON. | |
indirect enum JSONWrapper { | |
case string(String) | |
case int(Int) | |
case double(Double) | |
case boolean(Bool) | |
case object([String: JSONWrapper]) | |
case array([JSONWrapper]) | |
enum JSONWrapperError: Error { | |
case invalidValue(Any) | |
case failedToDecode(from: Decoder) | |
} | |
init(_ value: Any) throws { | |
if let string = value as? String { | |
self = .string(string) | |
return | |
} else if let int = value as? Int { | |
self = .int(int) | |
return | |
} else if let double = value as? Double { | |
self = .double(double) | |
return | |
} else if let boolean = value as? Bool { | |
self = .boolean(boolean) | |
return | |
} else if let object = value as? [String: Any] { | |
let wrapperObject = try object.mapValues { item -> JSONWrapper in | |
return try JSONWrapper(item) | |
} | |
self = .object(wrapperObject) | |
return | |
} else if let array = value as? [Any] { | |
let wrapperArray = try array.map { item in | |
return try JSONWrapper(item) | |
} | |
self = .array(wrapperArray) | |
return | |
} | |
throw JSONWrapperError.invalidValue(value) | |
} | |
func unwrap() -> Any { | |
switch self { | |
case .string(let string): | |
return string | |
case .int(let int): | |
return int | |
case .double(let double): | |
return double | |
case .boolean(let boolean): | |
return boolean | |
case .object(let object): | |
return object.mapValues { value in | |
return value.unwrap() | |
} | |
case .array(let array): | |
return array.map { value in | |
return value.unwrap() | |
} | |
} | |
} | |
} | |
extension JSONWrapper: Encodable { | |
func encode(to encoder: Encoder) throws { | |
var container = encoder.singleValueContainer() | |
switch self { | |
case .string(let string): | |
try container.encode(string) | |
case .int(let int): | |
try container.encode(int) | |
case .double(let double): | |
try container.encode(double) | |
case .boolean(let boolean): | |
try container.encode(boolean) | |
case .object(let object): | |
try container.encode(object) | |
case .array(let array): | |
try container.encode(array) | |
} | |
} | |
} | |
extension JSONWrapper: Decodable { | |
init(from decoder: Decoder) throws { | |
let container = try decoder.singleValueContainer() | |
if let string = try? container.decode(String.self) { | |
self = .string(string) | |
return | |
} else if let int = try? container.decode(Int.self) { | |
self = .int(int) | |
return | |
} else if let double = try? container.decode(Double.self) { | |
self = .double(double) | |
return | |
} else if let boolean = try? container.decode(Bool.self) { | |
self = .boolean(boolean) | |
return | |
} else if let array = try? container.decode([JSONWrapper].self) { | |
self = .array(array) | |
return | |
} else if let object = try? container.decode([String: JSONWrapper].self) { | |
self = .object(object) | |
return | |
} | |
throw JSONWrapperError.failedToDecode(from: decoder) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment