简体   繁体   English

如何手动解码选定的键并使用 swift Decodable 自动解码?

[英]How to Decode selected keys manually and rest with the automatic decoding with swift Decodable?

Here is the code I am using,这是我正在使用的代码,

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
        }

    }
}

Here, transcodeId is decided by either _transcode or _transcoder .这里, transcodeId_transcode_transcoder决定。 But I want id and rest of the keys (not included here) to be automatically decoded.但我希望自动解码id和其余键(不包括在此处)。 How can I do it ?我该怎么做 ?

You need to manually parse all the keys once you implement init(from:) in the Codable type.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
        }
    }
}

In the above code,在上面的代码中,

  1. In case you only want to decode the JSON , there is no need to use Codable .如果您只想解码JSON ,则无需使用Codable Using Decodable is enough.使用Decodable就足够了。
  2. Using multiple enums for CodingKey seems unnecessary here.在这里似乎没有必要为CodingKey使用多个enums You can use a single enum CodingKeys .您可以使用单个enum CodingKeys
  3. If the property name and the key name is an exact match, there is no need to explicitly specify the rawValue of that case in enum CodingKeys .如果属性名密钥名是完全匹配的,就没有必要明确指定rawValue是的caseenum CodingKeys So, there is no requirement of "_transcode" and "_transcoder" rawValues in TopLevelCodingKeys .因此, TopLevelCodingKeys不需要"_transcode""_transcoder" rawValues

Apart from all that, you can use keyDecodingStrategy as .convertFromSnakeCase to handle underscore notation ( snake case notation ), ie除此之外,您可以使用keyDecodingStrategy作为.convertFromSnakeCase来处理下划线符号(蛇大小写符号),即

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

So, you don't need to explicitly handle all the snake-case keys .因此,您不需要显式处理所有蛇形大小写的键 It'll be handled by the JSONDecoder on its own.它将由JSONDecoder自行处理。

This can be one of the good solution for you wherever you want you can add multiple keys for one variable:无论您在何处都可以为一个变量添加多个键,这对您来说都是不错的解决方案之一:

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
    }
}

Hope it helps.希望能帮助到你。

The compiler-generated init(from:) is all-or-nothing.编译器生成的init(from:)是全有或全无。 You can't have it decode some keys and “manually” decode others.您不能让它解码某些键并“手动”解码其他键。

One way to use the compiler-generated init(from:) is by giving your struct both of the possible encoded properties, and make transcodeId a computed property:使用编译器生成的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
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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