Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/json/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Json 如何将自定义密钥与Swift 4';什么是可解码协议?_Json_Swift_Swift4_Codable - Fatal编程技术网

Json 如何将自定义密钥与Swift 4';什么是可解码协议?

Json 如何将自定义密钥与Swift 4';什么是可解码协议?,json,swift,swift4,codable,Json,Swift,Swift4,Codable,Swift 4通过协议引入了对本机JSON编码和解码的支持。如何为此使用自定义密钥 例如,假设我有一个结构 struct Address:Codable { var street:String var zip:String var city:String var state:String } 我可以将其编码为JSON let address = Address(street: "Apple Bay Street", zip: "94608", city: "Em

Swift 4通过协议引入了对本机JSON编码和解码的支持。如何为此使用自定义密钥

例如,假设我有一个结构

struct Address:Codable {
    var street:String
    var zip:String
    var city:String
    var state:String
}
我可以将其编码为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" 
           }
    }
}
我可以把它编码回一个对象

    let newAddress: Address = try decoder.decode(Address.self, from: encoded)
但是如果我有一个json对象

{ 
   "state":"California", 
   "street":"Apple Bay Street", 
   "zip_code":"94608", 
   "city":"Emeryville" 
}
我如何告诉
地址
上的解码器
邮政编码
映射到
邮政编码
?我相信您使用了新的
编码密钥
协议,但我不知道如何使用它。

手动自定义编码密钥 在您的示例中,您将获得自动生成的一致性,因为您的所有属性也符合
Codable
。这种一致性会自动创建一个只与属性名相对应的密钥类型,然后使用该类型对单个密钥容器进行编码/解码

然而,这种自动生成的一致性的一个非常巧妙的特性是,如果您在类型中定义一个符合协议的嵌套
枚举
(或使用一个
类型别名
),Swift将自动将其用作密钥类型。因此,您可以轻松自定义属性编码/解码时使用的密钥

这意味着你可以说:

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
    }
}
枚举案例名称需要与属性名称匹配,并且这些案例的原始值需要与您编码/解码的键匹配(除非另有规定,否则
字符串
枚举的原始值将与案例名称相同)。因此,
zip
属性现在将使用键
“zip\u code”
进行编码/解码

自动生成/一致性的确切规则由(我的重点)详述:

除了自动
CodingKey
需求合成
枚举
可编码
&
可解码
需求可以自动 也针对某些类型合成:

  • 符合
    Encodable
    且其属性均为
    Encodable
    的类型将获得自动生成的
    String
    -backed
    CodingKey
    枚举映射 属性的大小写名称。类似地,对于其 属性都是可解码的

  • 类型分为(1)和手动提供
    编码键
    枚举
    (直接命名为
    编码键
    ,或通过
    类型别名
    )的类型 案例按名称1对1映射到
    可编码
    /
    可解码
    属性
    -获取 自动合成
    init(from:)
    编码(to:)
    ,视情况而定, 使用这些属性和键

  • 不属于(1)或(2)的类型必须在需要时提供自定义密钥类型,并提供自己的
    init(from:)
    根据需要编码(到:)

  • 编码示例:

    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"}
    
    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"}
    解码示例:

    // 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")
    
    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")

    属性名的自动
    snake\u case
    JSON键 在Swift 4.1中,如果您将
    zip
    属性重命名为
    zipCode
    ,您可以利用
    jsonecoder
    JSONDecoder
    上的密钥编码/解码策略,以便在
    camelCase
    snake\u case
    之间自动转换编码密钥

    编码示例:

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

    (这并没有严格地回答您的具体问题,但考虑到本问答的规范性,我觉得值得包括在内)

    自定义自动JSON密钥映射 在Swift 4.1中,您可以利用
    JSONECODER
    JSONDecoder
    上的自定义密钥编码/解码策略,从而提供映射编码密钥的自定义功能

    您提供的函数需要一个<代码> [CODIGKIKE] ,它代表了编码/解码中当前点的编码路径(在大多数情况下,您只需要考虑最后一个元素,即,当前密钥)。函数返回一个

    CodingKey
    ,它将替换此数组中的最后一个键

    例如,
    UpperCamelCase
    JSON键用于
    lowerCamelCase
    属性名称:

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

    您现在可以使用
    .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"}
    
    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")
    
    并使用
    .convertFromUpperCamelCase
    关键策略进行解码:

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

    使用Swift 4.2,根据您的需要,您可以使用以下三种策略之一,以使您的模型对象自定义属性名称与JSON键匹配


    #1.使用自定义编码键 使用以下实现声明符合
    Codable
    Decodable
    Encodable
    协议)的结构时

    struct Address: Codable {
        var street: String
        var zip: String
        var city: String
        var state: String        
    }
    
    。。。编译器会自动为您生成符合
    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
        }
    }
    
    因此,如果序列化数据格式中使用的键与数据类型中的属性名称不匹配,则可以手动实现此枚举,并为所需的情况设置适当的
    rawValue

    以下示例显示了如何执行此操作:

    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
        }
    }
    
    import Foundation
    
    struct Address: Codable {
        var street: String
        var zipCode: String
        var cityName: String
        var state: String
    }
    
    编码(将
    zip
    属性替换为“zip\u code”JSON键):

    解码(将“zip_code”JSON键替换为
    zip
    property):


    #2.使用snake-case对camel-case密钥编码策略 如果您的JSON具有蛇壳键,并且希望将它们转换为模型对象的骆驼壳属性,则可以将
    JSONEncoder
    JSONDecoder
    的属性设置为
    .convertToSnakeCase

    以下示例显示了如何执行此操作:

    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
        }
    }
    
    import Foundation
    
    struct Address: Codable {
        var street: String
        var zipCode: String
        var cityName: String
        var state: String
    }
    
    编码(将驼峰大小写的属性转换为蛇大小写的JSON键):

    解码(将蛇壳JSON键转换为骆驼壳属性):


    #3.使用自定义密钥编码策略 如果
    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")
     */
    
    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
        }
    }
    
    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"}
     */
    
    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")
     */
    
    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
    }
    
    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 = ""
            }
        }
    }
    
    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
        } }