简体   繁体   中英

Swift get json with Codable

I want get json by encode model, which inherit a base class, and conforms to protocol Codable . But it failed. Here is the code:

// this is a func to get json
func getJson<T>(model: T) throws -> String where T: AnyObject, T: Codable {
    let encoder = JSONEncoder()
    let data = try encoder.encode(model)
    if let json = String(data: data, encoding: String.Encoding.utf8) {
        return json
    } else {
        return ""
    }
}


class BaseClass: Codable {
    var bx = 0
}

class SubClass: BaseClass  {
    var x = 1
    var y = 2
}

// test if get json enable
func test1() throws {
    let source = SubClass()
    let json = try getJson(model: source)
    print(json)
}

// how can i get json by this code
func test2() throws {
    let source = SubClass()

    var any: BaseClass?
    any = source
    let json = try getJson(model: any!)
    print(json)
}

I modifed the code like below , then the func test1() backed right json, but func test2() was error :

class BaseClass {
    var bx = 0
}

class SubClass: BaseClass, Codable  {
    var x = 1
    var y = 2
}

Looking forward to your help !

Encodable and Decodable involve some code synthesis where the compiler essentially writes the code for you. When you conform BaseClass to Codable, these methods are written to the BaseClass class and hence they are not aware of any additional properties defined by SubClass. You have to override the encode(to:) method in your subclass:

class BaseClass: Codable {
    var bx = 0
}

class SubClass: BaseClass  {
    var x = 1
    var y = 2

    private enum CodingKeys: String, CodingKey {
        case x
        case y
    }

    override func encode(to encoder: Encoder) throws {
        try super.encode(to: encoder)
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(self.x, forKey: .x)
        try container.encode(self.y, forKey: .y)
    }
}

You can try like this

let url = URL(string: "http://www.stackoverflow.com")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in  
    let jsonDecoder = JSONDecoder()
    let responseModel = try jsonDecoder.decode(Json4Swift_Base.self, from: data!)

}
task.resume()

struct Json4Swift_Base : Codable {

let gameID : Int?
let start : String?

enum CodingKeys: String, CodingKey {

    case gameID = "gameID"
    case start = "start"

}

init(from decoder: Decoder) throws {
    let values = try decoder.container(keyedBy: CodingKeys.self)
    gameID = try values.decodeIfPresent(Int.self, forKey: .gameID)
    start = try values.decodeIfPresent(String.self, forKey: .start)
}

}

If you need more help this website is very helpful to convert your json models http://www.json4swift.com

I modified your code, you can check it out.

 func getJson<T>(model: T) throws -> String where T: AnyObject, T: Codable {
    let encoder = JSONEncoder()
    let data = try encoder.encode(model)
    if let json = String(data: data, encoding: String.Encoding.utf8) {
        return json
    } else {
        return ""
    }
}


class BaseClass: Codable {
    var x : Int
    var y: Int
    init(x bx: Int, y by: Int) {
        x = bx
        y = by
    }
}

class SubClass: BaseClass {
    override init(x bx: Int, y by: Int) {
        super.init(x: bx, y: by)
    }

    required init(from decoder: Decoder) throws {
        fatalError("init(from:) has not been implemented")
    }
}

// test if get json enable
func test1() throws {
    let source = SubClass(x: 1, y: 6)
    let json = try getJson(model: source)
    print(json)
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM