Any when decoding JSON with Codable?

后端 未结 4 1920
情深已故
情深已故 2020-12-06 08:32

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

4条回答
  •  陌清茗
    陌清茗 (楼主)
    2020-12-06 08:59

    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 :)

提交回复
热议问题