简体   繁体   English

如何在 Swift 4 的可解码协议中使用自定义键?

[英]How do I use custom keys with Swift 4's Decodable protocol?

Swift 4 introduced support for native JSON encoding and decoding via theDecodable protocol. Swift 4 通过可解码协议引入了对原生 JSON 编码和解码的Decodable How do I use custom keys for this?我如何为此使用自定义键?

Eg, say I have a struct例如,说我有一个结构

struct Address:Codable {
    var street:String
    var zip:String
    var city:String
    var state:String
}

I can encode this to JSON.我可以将其编码为 JSON。

let address = Address(street: "Apple Bay Street", zip: "94608", city: "Emeryville", state: "California")

if let encoded = try? encoder.encode(address) {
    if let json = String(data: encoded, encoding: .utf8) {
        // Print JSON String
        print(json)

        // JSON string is 
           { "state":"California", 
             "street":"Apple Bay Street", 
             "zip":"94608", 
             "city":"Emeryville" 
           }
    }
}

I can encode this back to an object.我可以将其编码回一个对象。

    let newAddress: Address = try decoder.decode(Address.self, from: encoded)

But If I had a json object that was但是如果我有一个 json 对象

{ 
   "state":"California", 
   "street":"Apple Bay Street", 
   "zip_code":"94608", 
   "city":"Emeryville" 
}

How would I tell the decoder on Address that zip_code maps to zip ?我如何告诉解码器zip_code映射到zip Address I believe you use the new CodingKey protocol, but I can't figure out how to use this.我相信你使用新的CodingKey协议,但我不知道如何使用它。

Manually customising coding keys手动自定义编码键

In your example, you're getting an auto-generated conformance to Codable as all your properties also conform to Codable .在您的示例中,您将获得自动生成的Codable一致性,因为您的所有属性也符合Codable This conformance automatically creates a key type that simply corresponds to the property names – which is then used in order to encode to/decode from a single keyed container.这种一致性会自动创建一个与属性名称简单对应的密钥类型——然后使用它来编码/解码单个密钥容器。

However one really neat feature of this auto-generated conformance is that if you define a nested enum in your type called " CodingKeys " (or use a typealias with this name) that conforms to theCodingKey protocol – Swift will automatically use this as the key type.但是,这种自动生成的一致性一个真正实用的功能是,如果你定义一个嵌套enum在您的类型,称为“ CodingKeys ”(或使用typealias使用该名称),一个符合CodingKey协议-斯威夫特将自动使用这个作为重点类型。 This therefore allows you to easily customise the keys that your properties are encoded/decoded with.因此,这允许您轻松自定义用于编码/解码属性的密钥。

So what this means is you can just say:所以这意味着你可以说:

struct Address : Codable {

    var street: String
    var zip: String
    var city: String
    var state: String

    private enum CodingKeys : String, CodingKey {
        case street, zip = "zip_code", city, state
    }
}

The enum case names need to match the property names, and the raw values of these cases need to match the keys that you're encoding to/decoding from (unless specified otherwise, the raw values of a String enumeration will the same as the case names).枚举 case 名称需要匹配属性名称,并且这些 case 的原始值需要匹配您编码/解码的键(除非另有说明,否则String枚举的原始值将与 case 相同名称)。 Therefore, the zip property will now be encoded/decoded using the key "zip_code" .因此,现在将使用密钥"zip_code"zip属性进行编码/解码。

The exact rules for the auto-generatedEncodable /Decodable conformance are detailed by the evolution proposal (emphasis mine):自动生成的可Encodable /可Decodable一致性的确切规则在进化提案中详细说明(重点是我的):

In addition to automatic CodingKey requirement synthesis for enums , Encodable & Decodable requirements can be automatically synthesized for certain types as well:除了enums自动CodingKey需求合成之外, Encodable以为某些类型自动合成可Encodable和可Decodable需求:

  1. Types conforming to Encodable whose properties are all Encodable get an automatically generated String -backed CodingKey enum mapping properties to case names.类型符合Encodable ,其性质都是Encodable得到一个自动生成的String -backed CodingKey枚举映射属性案例名称。 Similarly for Decodable types whose properties are all Decodable同样对于所有属性都是Decodable Decodable类型

  2. Types falling into (1) — and types which manually provide a CodingKey enum (named CodingKeys , directly, or via a typealias ) whose cases map 1-to-1 to Encodable / Decodable properties by name — get automatic synthesis of init(from:) and encode(to:) as appropriate, using those properties and keys属于 (1) 的类型-以及手动提供CodingKey enum (直接命名为CodingKeys ,或通过typealias )的类型,其情况按名称将 1 对 1 映射到可Encodable / 可Decodable属性- 自动合成init(from:)并适当地encode(to:) ,使用这些属性和键

  3. Types which fall into neither (1) nor (2) will have to provide a custom key type if needed and provide their own init(from:) and encode(to:) , as appropriate如果需要,既不属于 (1) 也不属于 (2) 的类型必须提供自定义键类型,并根据需要提供自己的init(from:)encode(to:)

Example encoding:示例编码:

import Foundation

let address = Address(street: "Apple Bay Street", zip: "94608",
                      city: "Emeryville", state: "California")

do {
    let encoded = try JSONEncoder().encode(address)
    print(String(decoding: encoded, as: UTF8.self))
} catch {
    print(error)
}
//{"state":"California","street":"Apple Bay Street","zip_code":"94608","city":"Emeryville"}

Example decoding:示例解码:

// using the """ multi-line string literal here, as introduced in SE-0168,
// to avoid escaping the quotation marks
let jsonString = """
{"state":"California","street":"Apple Bay Street","zip_code":"94608","city":"Emeryville"}
"""

do {
    let decoded = try JSONDecoder().decode(Address.self, from: Data(jsonString.utf8))
    print(decoded)
} catch {
    print(error)
}

// Address(street: "Apple Bay Street", zip: "94608",
// city: "Emeryville", state: "California")

Automatic snake_case JSON keys for camelCase property names snake_case camelCase属性名称的自动snake_case JSON键

In Swift 4.1, if you rename your zip property to zipCode , you can take advantage of the key encoding/decoding strategies on JSONEncoder and JSONDecoder in order to automatically convert coding keys between camelCase and snake_case .在 Swift 4.1 中,如果您将zip属性重命名为zipCode ,则可以利用JSONEncoderJSONDecoder上的关键编码/解码策略,以便在camelCasesnake_case之间自动转换编码键。

Example encoding:示例编码:

import Foundation

struct Address : Codable {
  var street: String
  var zipCode: String
  var city: String
  var state: String
}

let address = Address(street: "Apple Bay Street", zipCode: "94608",
                      city: "Emeryville", state: "California")

do {
  let encoder = JSONEncoder()
  encoder.keyEncodingStrategy = .convertToSnakeCase
  let encoded = try encoder.encode(address)
  print(String(decoding: encoded, as: UTF8.self))
} catch {
  print(error)
}
//{"state":"California","street":"Apple Bay Street","zip_code":"94608","city":"Emeryville"}

Example decoding:示例解码:

let jsonString = """
{"state":"California","street":"Apple Bay Street","zip_code":"94608","city":"Emeryville"}
"""

do {
  let decoder = JSONDecoder()
  decoder.keyDecodingStrategy = .convertFromSnakeCase
  let decoded = try decoder.decode(Address.self, from: Data(jsonString.utf8))
  print(decoded)
} catch {
  print(error)
}

// Address(street: "Apple Bay Street", zipCode: "94608",
// city: "Emeryville", state: "California")

One important thing to note about this strategy however is that it won't be able to round-trip some property names with acronyms or initialisms which, according to the Swift API design guidelines , should be uniformly upper or lower case (depending on the position).然而,关于此策略需要注意的一件重要事情是,它无法使用首字母缩略词或首字母缩写词来回传递某些属性名称,根据Swift API 设计指南,这些名称应统一为大写或小写(取决于位置) )。

For example, a property named someURL will be encoded with the key some_url , but on decoding, this will be transformed to someUrl .例如,名为someURL的属性将使用密钥some_url进行编码,但在解码时,这将转换为someUrl

To fix this, you'll have to manually specify the coding key for that property to be string that the decoder expects, eg someUrl in this case (which will still be transformed to some_url by the encoder):要解决此问题,您必须手动将该属性的编码键指定为解码器期望的字符串,例如在这种情况下的someUrl (它仍将被编码器转换为some_url ):

struct S : Codable {

  private enum CodingKeys : String, CodingKey {
    case someURL = "someUrl", someOtherProperty
  }

  var someURL: String
  var someOtherProperty: String
}

(This doesn't strictly answer your specific question, but given the canonical nature of this Q&A, I feel it's worth including) (这并不能严格回答您的具体问题,但鉴于此问答的规范性质,我觉得值得包括在内)

Custom automatic JSON key mapping自定义自动 JSON 键映射

In Swift 4.1, you can take advantage of the custom key encoding/decoding strategies on JSONEncoder and JSONDecoder , allowing you to provide a custom function to map coding keys.在 Swift 4.1 中,您可以利用JSONEncoderJSONDecoder上的自定义键编码/解码策略,允许您提供自定义函数来映射编码键。

The function you provide takes a [CodingKey] , which represents the coding path for the current point in encoding/decoding (in most cases, you'll only need to consider the last element; that is, the current key).您提供的函数采用[CodingKey] ,它表示编码/解码中当前点的编码路径(在大多数情况下,您只需要考虑最后一个元素;即当前键)。 The function returns a CodingKey that will replace the last key in this array.该函数返回一个CodingKey ,它将替换此数组中的最后一个键。

For example, UpperCamelCase JSON keys for lowerCamelCase property names:例如, lowerCamelCase属性名称的UpperCamelCase JSON 键:

import Foundation

// wrapper to allow us to substitute our mapped string keys.
struct AnyCodingKey : CodingKey {

  var stringValue: String
  var intValue: Int?

  init(_ base: CodingKey) {
    self.init(stringValue: base.stringValue, intValue: base.intValue)
  }

  init(stringValue: String) {
    self.stringValue = stringValue
  }

  init(intValue: Int) {
    self.stringValue = "\(intValue)"
    self.intValue = intValue
  }

  init(stringValue: String, intValue: Int?) {
    self.stringValue = stringValue
    self.intValue = intValue
  }
}

extension JSONEncoder.KeyEncodingStrategy {

  static var convertToUpperCamelCase: JSONEncoder.KeyEncodingStrategy {
    return .custom { codingKeys in

      var key = AnyCodingKey(codingKeys.last!)

      // uppercase first letter
      if let firstChar = key.stringValue.first {
        let i = key.stringValue.startIndex
        key.stringValue.replaceSubrange(
          i ... i, with: String(firstChar).uppercased()
        )
      }
      return key
    }
  }
}

extension JSONDecoder.KeyDecodingStrategy {

  static var convertFromUpperCamelCase: JSONDecoder.KeyDecodingStrategy {
    return .custom { codingKeys in

      var key = AnyCodingKey(codingKeys.last!)

      // lowercase first letter
      if let firstChar = key.stringValue.first {
        let i = key.stringValue.startIndex
        key.stringValue.replaceSubrange(
          i ... i, with: String(firstChar).lowercased()
        )
      }
      return key
    }
  }
}

You can now encode with the .convertToUpperCamelCase key strategy:您现在可以使用.convertToUpperCamelCase密钥策略进行编码:

let address = Address(street: "Apple Bay Street", zipCode: "94608",
                      city: "Emeryville", state: "California")

do {
  let encoder = JSONEncoder()
  encoder.keyEncodingStrategy = .convertToUpperCamelCase
  let encoded = try encoder.encode(address)
  print(String(decoding: encoded, as: UTF8.self))
} catch {
  print(error)
}
//{"Street":"Apple Bay Street","City":"Emeryville","State":"California","ZipCode":"94608"}

and decode with the .convertFromUpperCamelCase key strategy:并使用.convertFromUpperCamelCase密钥策略解码:

let jsonString = """
{"Street":"Apple Bay Street","City":"Emeryville","State":"California","ZipCode":"94608"}
"""

do {
  let decoder = JSONDecoder()
  decoder.keyDecodingStrategy = .convertFromUpperCamelCase
  let decoded = try decoder.decode(Address.self, from: Data(jsonString.utf8))
  print(decoded)
} catch {
  print(error)
}

// Address(street: "Apple Bay Street", zipCode: "94608",
// city: "Emeryville", state: "California")

With Swift 4.2, according to your needs, you may use one of the 3 following strategies in order to make your model objects custom property names match your JSON keys.使用 Swift 4.2,根据您的需要,您可以使用以下 3 种策略之一,以使您的模型对象自定义属性名称与您的 JSON 键匹配。


#1. #1. Using custom coding keys使用自定义编码键

When you declare a struct that conforms to Codable ( Decodable and Encodable protocols) with the following implementation...当您使用以下实现声明符合Codable (可Decodable和可Encodable协议)的结构时...

struct Address: Codable {
    var street: String
    var zip: String
    var city: String
    var state: String        
}

... the compiler automatically generates a nested enum that conforms to CodingKey protocol for you. ...编译器会自动为您生成符合CodingKey协议的嵌套枚举。

struct Address: Codable {
    var street: String
    var zip: String
    var city: String
    var state: String

    // compiler generated
    private enum CodingKeys: String, CodingKey {
        case street
        case zip
        case city
        case state
    }
}

Therefore, if the keys used in your serialized data format don't match the property names from your data type, you can manually implement this enum and set the appropriate rawValue for the required cases.因此,如果序列化数据格式中使用的键与数据类型中的属性名称不匹配,您可以手动实现此枚举并为所需情况设置适当的rawValue

The following example shows how to do:下面的例子展示了如何做:

import Foundation

struct Address: Codable {
    var street: String
    var zip: String
    var city: String
    var state: String

    private enum CodingKeys: String, CodingKey {
        case street
        case zip = "zip_code"
        case city
        case state
    }
}

Encode (replacing zip property with "zip_code" JSON key):编码(用“zip_code”JSON 密钥替换zip属性):

let address = Address(street: "Apple Bay Street", zip: "94608", city: "Emeryville", state: "California")

let encoder = JSONEncoder()
if let jsonData = try? encoder.encode(address), let jsonString = String(data: jsonData, encoding: .utf8) {
    print(jsonString)
}

/*
 prints:
 {"state":"California","street":"Apple Bay Street","zip_code":"94608","city":"Emeryville"}
 */

Decode (replacing "zip_code" JSON key with zip property):解码(用zip属性替换“zip_code”JSON 密钥):

let jsonString = """
{"state":"California","street":"Apple Bay Street","zip_code":"94608","city":"Emeryville"}
"""

let decoder = JSONDecoder()
if let jsonData = jsonString.data(using: .utf8), let address = try? decoder.decode(Address.self, from: jsonData) {
    print(address)
}

/*
 prints:
 Address(street: "Apple Bay Street", zip: "94608", city: "Emeryville", state: "California")
 */

#2. #2. Using snake case to camel case key coding strategies使用snake case到camel case的关键编码策略

If your JSON has snake-cased keys and you want to convert them to camel-cased properties for your model object, you can set your JSONEncoder 's keyEncodingStrategy and JSONDecoder 's keyDecodingStrategy properties to .convertToSnakeCase .如果您的 JSON 具有蛇形键,并且您想将它们转换为模型对象的骆驼形属性,则可以将JSONEncoderkeyEncodingStrategyJSONDecoderkeyDecodingStrategy属性设置为.convertToSnakeCase

The following example shows how to do:下面的例子展示了如何做:

import Foundation

struct Address: Codable {
    var street: String
    var zipCode: String
    var cityName: String
    var state: String
}

Encode (converting camel cased properties into snake cased JSON keys):编码(将骆驼壳属性转换为蛇壳 JSON 密钥):

let address = Address(street: "Apple Bay Street", zipCode: "94608", cityName: "Emeryville", state: "California")

let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
if let jsonData = try? encoder.encode(address), let jsonString = String(data: jsonData, encoding: .utf8) {
    print(jsonString)
}

/*
 prints:
 {"state":"California","street":"Apple Bay Street","zip_code":"94608","city_name":"Emeryville"}
 */

Decode (converting snake cased JSON keys into camel cased properties):解码(将蛇型 JSON 密钥转换为骆驼型属性):

let jsonString = """
{"state":"California","street":"Apple Bay Street","zip_code":"94608","city_name":"Emeryville"}
"""

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
if let jsonData = jsonString.data(using: .utf8), let address = try? decoder.decode(Address.self, from: jsonData) {
    print(address)
}

/*
 prints:
 Address(street: "Apple Bay Street", zipCode: "94608", cityName: "Emeryville", state: "California")
 */

#3. #3. Using custom key coding strategies使用自定义键编码策略

If necessary, JSONEncoder and JSONDecoder allow you to set a custom strategy to map coding keys using JSONEncoder.KeyEncodingStrategy.custom(_:) and JSONDecoder.KeyDecodingStrategy.custom(_:) .如有必要, JSONEncoderJSONDecoder允许您设置自定义策略以使用JSONEncoder.KeyEncodingStrategy.custom(_:)JSONDecoder.KeyDecodingStrategy.custom(_:)映射编码键。

The following example shows how to implement them:以下示例显示了如何实现它们:

import Foundation

struct Address: Codable {
    var street: String
    var zip: String
    var city: String
    var state: String
}

struct AnyKey: CodingKey {
    var stringValue: String
    var intValue: Int?

    init?(stringValue: String) {
        self.stringValue = stringValue
    }

    init?(intValue: Int) {
        self.stringValue = String(intValue)
        self.intValue = intValue
    }
}

Encode (converting lowercased first letter properties into uppercased first letter JSON keys):编码(将小写首字母属性转换为大写首字母 JSON 键):

let address = Address(street: "Apple Bay Street", zip: "94608", city: "Emeryville", state: "California")

let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .custom({ (keys) -> CodingKey in
    let lastKey = keys.last!
    guard lastKey.intValue == nil else { return lastKey }
    let stringValue = lastKey.stringValue.prefix(1).uppercased() + lastKey.stringValue.dropFirst()
    return AnyKey(stringValue: stringValue)!
})

if let jsonData = try? encoder.encode(address), let jsonString = String(data: jsonData, encoding: .utf8) {
    print(jsonString)
}

/*
 prints:
 {"Zip":"94608","Street":"Apple Bay Street","City":"Emeryville","State":"California"}
 */

Decode (converting uppercased first letter JSON keys into lowercased first letter properties):解码(将大写首字母 JSON 键转换为小写首字母属性):

let jsonString = """
{"State":"California","Street":"Apple Bay Street","Zip":"94608","City":"Emeryville"}
"""

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .custom({ (keys) -> CodingKey in
    let lastKey = keys.last!
    guard lastKey.intValue == nil else { return lastKey }
    let stringValue = lastKey.stringValue.prefix(1).lowercased() + lastKey.stringValue.dropFirst()
    return AnyKey(stringValue: stringValue)!
})

if let jsonData = jsonString.data(using: .utf8), let address = try? decoder.decode(Address.self, from: jsonData) {
    print(address)
}

/*
 prints:
 Address(street: "Apple Bay Street", zip: "94608", city: "Emeryville", state: "California")
 */

Sources:资料来源:

What I have done is create own structure just like what you are getting from the JSON with respect to its data types.我所做的是创建自己的结构,就像您从 JSON 中获得的数据类型一样。

Just like this:像这样:

struct Track {
let id : Int
let contributingArtistNames:String
let name : String
let albumName :String
let copyrightP:String
let copyrightC:String
let playlistCount:Int
let trackPopularity:Int
let playlistFollowerCount:Int
let artistFollowerCount : Int
let label : String
}

After this you need to create an extension of the same struct extending decodable and the enum of the same structure with CodingKey and then you need to initialize the decoder using this enum with its keys and datatypes (Keys will come from the enum and the datatypes will be coming or say referenced from the structure itself)在此之后,您需要使用CodingKey创建扩展decodable的相同struct扩展和相同structenum ,然后您需要使用此枚举及其键和数据类型来初始化解码器(键将来自枚举,数据类型将来自或说从结构本身引用)

extension Track: Decodable {

    enum TrackCodingKeys: String, CodingKey {
        case id = "id"
        case contributingArtistNames = "primaryArtistsNames"
        case spotifyId = "spotifyId"
        case name = "name"
        case albumName = "albumName"
        case albumImageUrl = "albumImageUrl"
        case copyrightP = "copyrightP"
        case copyrightC = "copyrightC"
        case playlistCount = "playlistCount"
        case trackPopularity = "trackPopularity"
        case playlistFollowerCount = "playlistFollowerCount"
        case artistFollowerCount = "artistFollowers"
        case label = "label"
    }
    init(from decoder: Decoder) throws {
        let trackContainer = try decoder.container(keyedBy: TrackCodingKeys.self)
        if trackContainer.contains(.id){
            id = try trackContainer.decode(Int.self, forKey: .id)
        }else{
            id = 0
        }
        if trackContainer.contains(.contributingArtistNames){
            contributingArtistNames = try trackContainer.decode(String.self, forKey: .contributingArtistNames)
        }else{
            contributingArtistNames = ""
        }
        if trackContainer.contains(.spotifyId){
            spotifyId = try trackContainer.decode(String.self, forKey: .spotifyId)
        }else{
            spotifyId = ""
        }
        if trackContainer.contains(.name){
            name = try trackContainer.decode(String.self, forKey: .name)
        }else{
            name = ""
        }
        if trackContainer.contains(.albumName){
            albumName = try trackContainer.decode(String.self, forKey: .albumName)
        }else{
            albumName = ""
        }
        if trackContainer.contains(.albumImageUrl){
            albumImageUrl = try trackContainer.decode(String.self, forKey: .albumImageUrl)
        }else{
            albumImageUrl = ""
        }
        if trackContainer.contains(.copyrightP){
            copyrightP = try trackContainer.decode(String.self, forKey: .copyrightP)
        }else{
            copyrightP = ""
        }
        if trackContainer.contains(.copyrightC){
                copyrightC = try trackContainer.decode(String.self, forKey: .copyrightC)
        }else{
            copyrightC = ""
        }
        if trackContainer.contains(.playlistCount){
            playlistCount = try trackContainer.decode(Int.self, forKey: .playlistCount)
        }else{
            playlistCount = 0
        }

        if trackContainer.contains(.trackPopularity){
            trackPopularity = try trackContainer.decode(Int.self, forKey: .trackPopularity)
        }else{
            trackPopularity = 0
        }
        if trackContainer.contains(.playlistFollowerCount){
            playlistFollowerCount = try trackContainer.decode(Int.self, forKey: .playlistFollowerCount)
        }else{
            playlistFollowerCount = 0
        }

        if trackContainer.contains(.artistFollowerCount){
            artistFollowerCount = try trackContainer.decode(Int.self, forKey: .artistFollowerCount)
        }else{
            artistFollowerCount = 0
        }
        if trackContainer.contains(.label){
            label = try trackContainer.decode(String.self, forKey: .label)
        }else{
            label = ""
        }
    }
}

You need to change here each and every key and datatypes according to your needs and use it with the decoder.您需要根据需要在此处更改每个键和数据类型,并将其与解码器一起使用。

By using CodingKey you can use custom keys in codable or decodable protocol.通过使用CodingKey,您可以在可编码或可解码协议中使用自定义密钥。

struct person: Codable {
    var name: String
    var age: Int
    var street: String
    var state: String

    private enum CodingKeys: String, CodingKey {
        case name
        case age
        case street = "Street_name"
        case state
    } }

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

相关问题 如何使用Decodable协议将此JSON转换为Swift结构? - How do I convert this JSON into a Swift structure using the Decodable protocol? 如何使用快速可解码协议解析嵌套的JSON数据? - how to use swift decodable protocol to parse nested JSON data? 如何使用 Swift 从一组 UUID 键中解码为 map 关系? - How to use Swift Decodable to map relationships from an array of UUID keys? 如果我在 swift 的结构中使用协议类型,我的结构不符合协议“可解码”/“可编码” - My structure does not conform to protocol 'Decodable' / 'Encodable' if I use protocol type in my structure in swift 如何将(Swift4)init添加到Decodable协议 - How to add a (Swift4) init to the Decodable protocol 如何使用Swift Decodable协议解析JSON API结构中的几个硬编码键? - How to parse several hardcoded keys in JSON API struct with Swift Decodable protocol? 如何使用可分解协议为具有不同键的相同Json属性创建通用类 - How to use Decodable Protocol to create common class for same Json properties with different keys 使用可解码协议在Swift 4中解码Google自定义搜索API - Decoding Google custom search API in Swift 4 using decodable protocol 使用Swift的Decodable协议解析CoinMarketCap API - Parsing CoinMarketCap API using Swift's Decodable protocol 将可解码协议与多个键一起使用 - Using Decodable protocol with multiples keys
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM