我已阅读How to decode a nested JSON struct with Swift Decodable protocol?它没有解决我的特定用例,其中字符串文字数值用作根字典。
Imanou Petit也回答How to decode a nested JSON struct with Swift Decodable protocol?。来自Leo Dabus的Can't decode JSON data from API回答。
货币是字典本身由data
字典中的文字字符串数字表示,所以这让我失望。我正在寻找使用枚举的最Swifty 4模型,其中很容易看到哪些容器对应于哪些字典。
P.S。 David Berry在下面给出了一个很好的答案,我已经实施了。如果其他人有其他方法来达到相同的结果,我很乐意看到不同的建议。也许有一些较新的Swift 4方法尚不为人所知或其他设计模式。
代码
struct RawServerResponse {
enum RootKeys: String, CodingKey {
case data
case btc = "1"
case eth = "1027"
case iota = "1720"
case ripple = "52"
case neo = "1376"
case quotes
case USD
}
enum BaseKeys: String, CodingKey {
case id, name, symbol, maxSupply = "max_supply"
}
enum QuotesKeys: String, CodingKey {
case USD
}
enum USDKeys: String, CodingKey {
case price, marketCap = "market_cap"
}
let data: String
let id: Int
let name: String
let symbol: String
let maxSupply: Double
let price: Double
let marketCap: Double
}
extension RawServerResponse: Decodable {
init(from decoder: Decoder) throws {
// data
let container = try decoder.container(keyedBy: RootKeys.self)
data = try container.decode(String.self, forKey: .data)
// id
let idContainer = try container.nestedContainer(keyedBy: BaseKeys.self, forKey: .data)
id = try idContainer.decode(Int.self, forKey: .id)
// price
let priceContainer = try container.nestedContainer(keyedBy: USDKeys.self, forKey: .USD)
price = try priceContainer.decode(Double.self, forKey: .price)
}
}
API / JSON https://api.coinmarketcap.com/v2/ticker/
{
"data": {
"1": {
"id": 1,
"name": "Bitcoin",
"symbol": "BTC",
"website_slug": "bitcoin",
"rank": 1,
"circulating_supply": 17041575.0,
"total_supply": 17041575.0,
"max_supply": 21000000.0,
"quotes": {
"USD": {
"price": 8214.7,
"volume_24h": 5473430000.0,
"market_cap": 139991426153.0,
"percent_change_1h": 0.09,
"percent_change_24h": 2.29,
"percent_change_7d": -2.44
}
},
"last_updated": 1526699671
},
"1027": {
"id": 1027,
"name": "Ethereum",
"symbol": "ETH",
"website_slug": "ethereum",
"rank": 2,
"circulating_supply": 99524121.0,
"total_supply": 99524121.0,
"max_supply": null,
"quotes": {
"USD": {
"price": 689.891,
"volume_24h": 2166100000.0,
"market_cap": 68660795252.0,
"percent_change_1h": 0.13,
"percent_change_24h": 2.51,
"percent_change_7d": 2.54
}
},
"last_updated": 1526699662
}
}
答案 0 :(得分:1)
我采用更简单的数据方法,查看"数据"作为一系列关键的,相同的回答,同样,"引用"是一个关键的行情集合。
struct RawServerResponse : Decodable {
enum Keys : String, CodingKey {
case data = "data"
}
let data : [String:Base]
}
struct Base : Decodable {
enum CodingKeys : String, CodingKey {
case id = "id"
case name = "name"
case symbol = "symbol"
case maxSupply = "max_supply"
case quotes = "quotes"
}
let id : Int64
let name : String
let symbol : String
let maxSupply : Double?
let quotes : [String:Quote]
}
struct Quote : Decodable {
enum CodingKeys : String, CodingKey {
case price = "price"
case marketCap = "market_cap"
}
let price : Double
let marketCap : Double
}
然后,如果您确实需要从这些更简单的结构中访问各个键控值,则可以提供计算出的访问器:
extension RawServerResponse {
enum BaseKeys : String {
case btc = "1"
case eth = "1027"
}
var eth : Base? { return data[BaseKeys.eth.rawValue] }
var btc : Base? { return data[BaseKeys.btc.rawValue] }
}
同样,您可以为货币创建类似的访问者:
extension Base {
enum Currencies : String {
case usd = "USD"
}
var usd : Quote? { return quotes[Currencies.usd.rawValue]}
}
一旦你确定了这部分内容,那么原始问题中的this link将告诉你如何将结构展平,如果这是你想要的。从本质上讲,它可以归结为将计算属性更改为您将设置为构造函数一部分的let
属性。