首页 > 解决方案 > 如何在swift中解码具有不同数据类型的不断变化的密钥

问题描述

我在将 json 解码为 Swift 中的 Codable 模型时遇到了一些问题。我从服务器得到的 json 响应看起来像这样

{
  "code": 200,
  "data": {
    "Australia": {
      "address": "1111",
      "port": "443",
      "proto": "udp",
      "country": "au"
    },
    "Australia udp": {
      "address": "2222",
      "port": "443",
      "proto": "udp",
      "country": "au"
    },
    "Vietnam TCP": {
      "address": "3333",
      "port": "443",
      "proto": "tcp",
      "country": "vn"
    },
    "status": "1"
  }
}

现在我找到了其他关于如何解码简单数据类型的帖子,但我找不到可以解码复杂数据类型的帖子!到目前为止我所做的是这个模型

struct ListServers: Codable {
    let code: Int
    let data: [String: ListServersData]
}

enum ListServersData: Codable {
    case string(String)
    case innerItem(ServerInfo)
    
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let x = try? container.decode(String.self) {
            self = .string(x)
            return
        }
        if let x = try? container.decode(ServerInfo.self) {
            self = .innerItem(x)
            return
        }
        throw DecodingError.typeMismatch(ListServersData.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for MyValue"))
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .string(let x):
            try container.encode(x)
        case .innerItem(let x):
            try container.encode(x)
        }
    }
}

struct ServerInfo: Codable {
    let address, port, proto, country: String
}

但这会引发错误!

我不确定如何解码,因为我有响应的附加状态

标签: iosjsonswift

解决方案


尝试这个:

只需在某处设置一个json字符串

let json =
"""
{
  "code": 200,
  "data": {
    "Australia": {
      "address": "1111",
      "port": "443",
      "proto": "udp",
      "country": "au"
    },
    "Australia udp": {
      "address": "2222",
      "port": "443",
      "proto": "udp",
      "country": "au"
    },
    "Vietnam TCP": {
      "address": "3333",
      "port": "443",
      "proto": "tcp",
      "country": "vn"
    },
    "status": "1"
  }
}
"""

然后设置与上面完全相同的结构和枚举:

struct ListServers: Codable {
    let code: Int
    let data: [String: ListServersData]
}

enum ListServersData: Codable {
    case string(String)
    case innerItem(ServerInfo)
    
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let x = try? container.decode(String.self) {
            self = .string(x)
            return
        }
        if let x = try? container.decode(ServerInfo.self) {
            self = .innerItem(x)
            return
        }
        throw DecodingError.typeMismatch(ListServersData.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for MyValue"))
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .string(let x):
            try container.encode(x)
        case .innerItem(let x):
            try container.encode(x)
        }
    }
}

struct ServerInfo: Codable {
    let address, port, proto, country: String
}

然后像这样解码那个json:

let jsonData = json.data(using: .utf8)!

do {
    let decoder = JSONDecoder()
    let result = try decoder.decode(ListServers.self, from: jsonData)
    print("Code: \(result.code)")
    
    for (_, v) in result.data {
        switch v {
        case .string(let string):
            print("Status: \(string)")
            
        case .innerItem(let serverInfo):
            print("Address: \(serverInfo.address)")
            print("Port: \(serverInfo.port)")
            print("Proto: \(serverInfo.proto)")
            print("Country: \(serverInfo.country)")
        }
        
        print("=======================")
    }
}
catch {
    print(error)
}

这是打印出来的没有错误的内容:

Code: 200
Address: 3333
Port: 443
Proto: tcp
Country: vn
=======================
Address: 2222
Port: 443
Proto: udp
Country: au
=======================
Status: 1
=======================
Address: 1111
Port: 443
Proto: udp
Country: au
=======================

推荐阅读