With Swift 3 JSONSerialization, if a part of your data model was completely dynamic, you could always just leave the deserialized data at Any and l
I solved my problem creating an AnyValue struct to allow Encode and Decode Any values from JSON:
To use it is pretty simple:
class MyClass: Codable {
var data: [String: AnyValue?]?
init(data: [String: AnyValue?]) {
self.data = data
}
}
let data = ["a": AnyValue(3), "b": AnyValue(true), "c": AnyValue("Rodrigo"), "d": AnyValue(3.3)]
let myClass = MyClass(data: data)
if let json = JsonUtil.toJson(myClass) {
print(json) // {"data":{"d":3.3,"b":true,"c":"Rodrigo","a":3}}
if let data = JsonUtil.from(json: json)?.data {
print(data["a"]??.value() ?? "nil") // 3
print(data["b"]??.value() ?? "nil") // true
print(data["c"]??.value() ?? "nil") // Rodrigo
print(data["d"]??.value() ?? "nil") // 3.3
}
}
AnyValue struct:
struct AnyValue: Codable {
private var int: Int?
private var string: String?
private var bool: Bool?
private var double: Double?
init(_ int: Int) {
self.int = int
}
init(_ string: String) {
self.string = string
}
init(_ bool: Bool) {
self.bool = bool
}
init(_ double: Double) {
self.double = double
}
init(from decoder: Decoder) throws {
if let int = try? decoder.singleValueContainer().decode(Int.self) {
self.int = int
return
}
if let string = try? decoder.singleValueContainer().decode(String.self) {
self.string = string
return
}
if let bool = try? decoder.singleValueContainer().decode(Bool.self) {
self.bool = bool
return
}
if let double = try? decoder.singleValueContainer().decode(Double.self) {
self.double = double
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
if let anyValue = self.value() {
if let value = anyValue as? Int {
try container.encode(value)
return
}
if let value = anyValue as? String {
try container.encode(value)
return
}
if let value = anyValue as? Bool {
try container.encode(value)
return
}
if let value = anyValue as? Double {
try container.encode(value)
return
}
}
try container.encodeNil()
}
func value() -> Any? {
return self.int ?? self.string ?? self.bool ?? self.double
}
}
And I created a JsonUtil struct too:
struct JsonUtil {
public static func from(json: String) -> T? {
if let jsonData = json.data(using: .utf8) {
let jsonDecoder = JSONDecoder()
do {
return try jsonDecoder.decode(T.self, from: jsonData)
} catch {
print(error)
}
}
return nil
}
public static func toJson(_ obj: T) -> String? {
let jsonEncoder = JSONEncoder()
do {
let jsonData = try jsonEncoder.encode(obj)
return String(data: jsonData, encoding: String.Encoding.utf8)
} catch {
print(error)
return nil
}
}
}
If you need a new type like [String] per example, you just need add it on AnyValue struct.
Good luck :)