使用Codable在一个模型类中解码两个不同的JSON响应



根据需求,我从api得到了两种不同的响应。那是

{
  "shopname":"xxx",
  "quantity":4,
  "id":1,
  "price":200.00,
}

另一个响应

{
  "storename":"xxx",
  "qty":4,
  "id":1,
  "amount":200.00,
}

这里,两个json值都在同一个模型类中进行解码。请帮我解决这个问题。

是否可以在单个变量中设置值,如数量,这两个变量都基于关键参数可用性存储在同一变量中

这里有一种方法,可以让您在代码中只有一个属性,而不是两个选项:

定义一个结构,该结构包含您需要的所有属性,以及您希望在代码中使用的名称。然后,定义两个CodingKey枚举,将这些属性映射到两种不同的JSON格式,并实现一个自定义初始化器:

let json1 = """
{
    "shopname":"xxx",
    "quantity":4,
    "id":1,
    "price":200.00,
}
""".data(using: .utf8)!
let json2 = """
{
    "storename":"xxx",
    "qty":4,
    "id":1,
    "amount":200.00,
}
""".data(using: .utf8)!
struct DecodingError: Error {}
struct Model: Decodable {
    let storename: String
    let quantity: Int
    let id: Int
    let price: Double
    enum CodingKeys1: String, CodingKey {
        case storename = "shopname"
        case quantity
        case id
        case price
    }
    enum CodingKeys2: String, CodingKey {
        case storename
        case quantity = "qty"
        case id
        case price = "amount"
    }
    init(from decoder: Decoder) throws {
        let container1 = try decoder.container(keyedBy: CodingKeys1.self)
        let container2 = try decoder.container(keyedBy: CodingKeys2.self)
        if let storename = try container1.decodeIfPresent(String.self, forKey: CodingKeys1.storename) {
            self.storename = storename
            self.quantity = try container1.decode(Int.self, forKey: CodingKeys1.quantity)
            self.id = try container1.decode(Int.self, forKey: CodingKeys1.id)
            self.price = try container1.decode(Double.self, forKey: CodingKeys1.price)
        } else if let storename = try container2.decodeIfPresent(String.self, forKey: CodingKeys2.storename) {
            self.storename = storename
            self.quantity = try container2.decode(Int.self, forKey: CodingKeys2.quantity)
            self.id = try container2.decode(Int.self, forKey: CodingKeys2.id)
            self.price = try container2.decode(Double.self, forKey: CodingKeys2.price)
        } else {
            throw DecodingError()
        }
    }
}

do {
    let j1 = try JSONDecoder().decode(Model.self, from: json1)
    print(j1)
    let j2 = try JSONDecoder().decode(Model.self, from: json2)
    print(j2)
} catch {
    print(error)
}

在单个模型中处理不同的密钥名称

下面是两个示例json(字典(,它们有一些常见的键(一个、两个(和一些不同的键(用于相同的错误目的(。

示例json:

let error_json:[String: Any] = [
    "error_code": 404,                  //different
    "error_message": "file not found",  //different
    "one":1, //common
    "two":2  //common
]
let failure_json:[String: Any] = [
    "failure_code": 404,                 //different
    "failure_message": "file not found", //different
    "one":1, //common
    "two":2  //common
]

CommonModel

struct CommonModel : Decodable {
    var code: Int?
    var message: String?
    var one:Int  //common
    var two:Int? //common
    
    private enum CodingKeys: String, CodingKey{ //common
        case one, two
    }
    private enum Error_CodingKeys : String, CodingKey {
        case  code = "error_code", message = "error_message"
    }
    private enum Failure_CodingKeys : String, CodingKey {
        case  code = "failure_code", message = "failure_message"
    }
    init(from decoder: Decoder) throws {
        let commonValues =  try decoder.container(keyedBy: CodingKeys.self)
        let errors = try decoder.container(keyedBy: Error_CodingKeys.self)
        let failures = try decoder.container(keyedBy: Failure_CodingKeys.self)
        
        ///common
        self.one = try commonValues.decodeIfPresent(Int.self, forKey: .one)!
        self.two = try commonValues.decodeIfPresent(Int.self, forKey: .two)
        /// different
        if errors.allKeys.count > 0{
            self.code = try errors.decodeIfPresent(Int.self, forKey: .code)
            self.message = try errors.decodeIfPresent(String.self, forKey: .message)
        }
        if failures.allKeys.count > 0{
            self.code = try failures.decodeIfPresent(Int.self, forKey: .code)
            self.message = try failures.decodeIfPresent(String.self, forKey: .message)
        }
    }
}

下面的扩展将帮助您将字典转换为数据。

public extension Decodable {
    init(from: Any) throws {
        let data = try JSONSerialization.data(withJSONObject: from, options: .prettyPrinted)
        let decoder = JSONDecoder()
        self = try decoder.decode(Self.self, from: data)
    }
}

测试

public func Test_codeble(){
    do {
         let err_obj = try CommonModel(from: error_json)
         print(err_obj)
         let failed_obj = try CommonModel(from: failure_json)
         print(failed_obj)
        
    }catch let error {
        print(error.localizedDescription)
    }
}

像一样使用

struct modelClass : Codable {
    let amount : Float?
    let id : Int?
    let price : Float?
    let qty : Int?
    let quantity : Int?
    let shopname : String?
    let storename : String?

    enum CodingKeys: String, CodingKey {
        case amount = "amount"
        case id = "id"
        case price = "price"
        case qty = "qty"
        case quantity = "quantity"
        case shopname = "shopname"
        case storename = "storename"
    }
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        amount = try values.decodeIfPresent(Float.self, forKey: .amount)
        id = try values.decodeIfPresent(Int.self, forKey: .id)
        price = try values.decodeIfPresent(Float.self, forKey: .price)
        qty = try values.decodeIfPresent(Int.self, forKey: .qty)
        quantity = try values.decodeIfPresent(Int.self, forKey: .quantity)
        shopname = try values.decodeIfPresent(String.self, forKey: .shopname)
        storename = try values.decodeIfPresent(String.self, forKey: .storename)
    }

}

相关内容

  • 没有找到相关文章

最新更新