【问题标题】:How to Decode selected keys manually and rest with the automatic decoding with swift Decodable?如何手动解码选定的键并使用 swift Decodable 进行自动解码?
【发布时间】:2019-07-26 07:55:55
【问题描述】:

这是我正在使用的代码,

struct CreatePostResponseModel : Codable{
    var transcodeId:String?
    var id:String = ""
    enum TopLevelCodingKeys: String, CodingKey {
        case _transcode = "_transcode"
        case _transcoder = "_transcoder"
    }
    enum CodingKeys:String, CodingKey{
        case id = "_id"
    }
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: TopLevelCodingKeys.self)
        if let transcodeId = try container.decodeIfPresent(String.self, forKey: ._transcode) {
            self.transcodeId = transcodeId
        }else if let transcodeId = try container.decodeIfPresent(String.self, forKey: ._transcoder) {
            self.transcodeId = transcodeId
        }

    }
}

这里,transcodeId_transcode_transcoder 决定。 但我希望 id 和其余的键(不包括在这里)被自动解码。我该怎么做?

【问题讨论】:

  • 您还必须手动解码其他键。

标签: ios swift codable decodable encodable


【解决方案1】:

Codable 类型中实现init(from:) 后,您需要手动解析所有键。

struct CreatePostResponseModel: Decodable {
    var transcodeId: String?
    var id: String

    enum CodingKeys:String, CodingKey{
        case id, transcode, transcoder
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decodeIfPresent(String.self, forKey: .id) ?? ""
        if let transcodeId = try container.decodeIfPresent(String.self, forKey: .transcode) {
            self.transcodeId = transcodeId
        } else if let transcodeId = try container.decodeIfPresent(String.self, forKey: .transcoder) {
            self.transcodeId = transcodeId
        }
    }
}

在上面的代码中,

  1. 如果您只想解码 JSON,则无需使用Codable。使用Decodable 就足够了。
  2. 在这里似乎没有必要对CodingKey 使用多个enums。您可以使用单个 enum CodingKeys
  3. 如果 property namekey name 完全匹配,则无需在 enum CodingKeys 中显式指定 caserawValue .所以,TopLevelCodingKeys 中不需要"_transcode""_transcoder" rawValues

除此之外,您还可以将keyDecodingStrategy 用作.convertFromSnakeCase 来处理下划线表示法(snake case notation),即

do {
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase //here.....
    let model = try decoder.decode(CreatePostResponseModel.self, from: data)
    print(model)
} catch {
    print(error)
}

因此,您无需显式处理所有 snake-case 键。它将由JSONDecoder 自行处理。

【讨论】:

  • 感谢您的回复,对于第 1 点:我也需要可编码,反之亦然。 2:如果我在 CodingKeys 中保留更多键并且没有相应的变量,则会出现编译错误。 3:属性名称来自两个值(_transcoder 和 _transcode)之一。
  • 不完全是,无论如何我必须解码所有的键:(
  • 是的,你必须这样做。最好修改 API 以提供一致的响应。
  • 完全同意。
【解决方案2】:

这对于您来说是一种很好的解决方案,您可以在任何地方为一个变量添加多个键:

var transcodeId:String?

public init(from decoder: Decoder) throws {

    do {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        transcodeId =  container.getValueFromAvailableKey(codingKeys: [CodingKeys._transcoder,CodingKeys._transcode])
    } catch {
        print("Error reading config file: \(error.localizedDescription)")
    }
}

extension KeyedDecodingContainerProtocol{

    func getValueFromAvailableKey(codingKeys:[CodingKey])-> String?{
         for key in codingKeys{
             for keyPath in self.allKeys{
                 if key.stringValue == keyPath.stringValue{
                    do{ 
                        return try self.decodeIfPresent(String.self, forKey: keyPath)
                    } catch {
                        return nil
                    }
                }
            }
        }
        return nil
    }
}

希望对你有帮助。

【讨论】:

    【解决方案3】:

    编译器生成的init(from:) 是全有或全无。您不能让它解码某些键并“手动”解码其他键。

    使用编译器生成的init(from:) 的一种方法是提供struct 两个可能的编码属性,并使transcodeId 成为计算属性:

    struct CreatePostResponseModel: Codable {
        var transcodeId: String? {
            get { _transcode ?? _transcoder }
            set { _transcode = newValue; _transcoder = nil }
        }
    
        var _transcode: String? = nil
        var _transcoder: String? = nil
    
        var id: String = “”
        // other properties
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-10-27
      • 1970-01-01
      • 2018-01-19
      • 1970-01-01
      • 2019-03-13
      • 2021-08-07
      • 2021-05-24
      相关资源
      最近更新 更多