【问题标题】:Dynamic Codable model, bind during runtime动态 Codable 模型,在运行时绑定
【发布时间】:2023-02-09 08:38:34
【问题描述】:

Codable 结构创建,根据响应解码,响应上的一些键。但是 api 响应发送的密钥决定了在父 json 中使用的模型。如何在内部访问它:下面的演示代码..

如果值为“First”,那么我想使用 BodyResponse1,在所有其他情况下使用 BodyResponse2

public protocol BodyResponse: Codable { }

struct BodyResponse1: BodyResponse {
    let title_1: String
    let description_1: String
}

struct BodyResponse2: BodyResponse {
    let title_2: String
    let description_2: String
}

struct BaseModel {
    let key: String
    let child_model: ChildModel?
}


struct ChildModel {
    let body: BodyResponse?

    enum CodingKeys: String, CodingKey {
        case body
    }
    
    public init(from decoder: Decoder) throws {
        
        let container = try decoder.container(keyedBy: CodingKeys.self)

        let value = "" // access the value for key in base model: how
        if value == "First" {
            body = try? BodyResponse1.init(from: container.superDecoder(forKey: .body))
        } else {
            body = try? BodyResponse2.init(from: container.superDecoder(forKey: .body))
        }
    }
}

示例 json:

{
  "key": "First",
    "child_model": {
      "body": {
        "title_1": "",
        "description_1": ""
      },
      "random_key": 12
    }
}

{
  "key": "Second",
    "child_model": {
      "body": {
        "title_2": "",
        "description_2": ""
      },
      "random_key": 12
    }
}

【问题讨论】:

  • 您知道所有可以归还的钥匙吗?或者它也是动态的?
  • body 内的键将有 body1 或 body2

标签: json swift decoding


【解决方案1】:

您可以查看BaseModel中的密钥。使用nestedContainer 获取子模型的容器。

struct BaseModel: Codable {
    let key: String
    let childModel: ChildModel?
    
    enum CodingKeys: String, CodingKey {
        case key, childModel = "child_model"
    }
    
    enum ChildCodingKeys: CodingKey {
        case body
    }
    
    public init(from decoder: Decoder) throws {
        
        let container = try decoder.container(keyedBy: CodingKeys.self)
        key = try container.decode(String.self, forKey: .key)
        let childContainer = try container.nestedContainer(keyedBy: ChildCodingKeys.self, forKey: .childModel)
        if key == "First" {
            childModel = ChildModel(body: try childContainer.decode(BodyResponse1.self, forKey: .body))
        } else if key == "Second" {
            childModel = ChildModel(body: try childContainer.decode(BodyResponse2.self, forKey: .body))
        } else {
            throw DecodingError.dataCorruptedError(forKey: .body, in: childContainer, debugDescription: "Unknown body response")
        }
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(key, forKey: .key)
        var childContainer = container.nestedContainer(keyedBy: ChildCodingKeys.self, forKey: .childModel)
        if let response1 = childModel?.body as? BodyResponse1 {
            try childContainer.encode(response1, forKey: .body)
        } else if let response2 = childModel?.body as? BodyResponse2 {
            try childContainer.encode(response2, forKey: .body)
        } else {
            throw EncodingError.invalidValue(childModel?.body as Any, .init(codingPath: childContainer.codingPath, debugDescription: "Unknown body response type"))
        }
    }
}

// you might want to move "body" into BaseModel if ChildModel only has this one property.
struct ChildModel {
    let body: BodyResponse?
}

【讨论】:

  • 此外,如果您已经知道期望的键,您还可以将 key 作为枚举类型以更好地使用。
  • 我们也可以做类似的编码器吗?
  • @Saranjith 查看编辑。
猜你喜欢
  • 1970-01-01
  • 2015-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-03
  • 1970-01-01
  • 2014-03-05
相关资源
最近更新 更多