如何使用 Codable 解码具有更改键的 json 响应?
Posted
技术标签:
【中文标题】如何使用 Codable 解码具有更改键的 json 响应?【英文标题】:How do I use Codable to decode json response which has changing keys? 【发布时间】:2019-05-29 10:13:43 【问题描述】:我收到了来自 fixer api 的 json 响应。我想将它解码为如下所示的结构。问题是 json 中“rates”的值是一个键值对数组,它有不同的键。
我有第二个结构,必须从这些键值映射。
我如何使用Codable
来做到这一点?
"success": true,
"timestamp": 1558883585,
"base": "EUR",
"date": "2019-05-26",
"rates":
"AED": 4.116371,
"AFN": 90.160103,
"ALL": 122.341655,
"AMD": 536.359254,
"ANG": 2.097299,
"AOA": 368.543773,
"ARS": 50.418429,
"AUD": 1.618263,
"AWG": 2.012124,
"AZN": 1.910752,
"BAM": 1.955812,
"BBD": 2.227793,
"BDT": 94.245988,
"BGN": 1.956097,
"BHD": 0.421705,
"BIF": 2051.459244,
"BMD": 1.120649,
"BND": 1.539664,
"BOB": 7.729394,
"BRL": 4.508038,
"BSD": 1.118587,
"BTC": 0.00014,
"BTN": 77.755286,
"BWP": 12.040813,
"BYN": 2.323273,
"BYR": 21964.71167,
"BZD": 2.254689
如果我如下更改“ExchangeRate”结构,它很容易解码。但我有“汇率”应该是“ConvertedRate”结构数组的要求。
struct ExchangeRate : Codable
let base : String
let date : String
let rates : [String:Double]
这是我需要的。
struct ExchangeRate : Codable
let base : String
let date : String
let rates : [ConvertedRate] //keys are varied in json
struct ConvertedRate : Codable, Comparable
let currencyName : String
let rate : Double
【问题讨论】:
【参考方案1】:您必须编写自定义 init(from:)
,因为您没有使用“默认行为”。
有关Apple documentation的更多信息。
这是一个可能的解决方案:
struct ExchangeRate : Codable
let base : String
let date : String
let rates : [ConvertedRate]
enum CodingKeys: String, CodingKey
case base
case date
case rates
init(from decoder: Decoder) throws
let values = try decoder.container(keyedBy: CodingKeys.self)
base = try values.decode(String.self, forKey: .base)
date = try values.decode(String.self, forKey: .date)
let additionalInfo = try values.decode([String: Double].self, forKey: .rates)
print(additionalInfo)
rates = additionalInfo.map( ConvertedRate(name: $0.key, rate: $0.value) )
struct ConvertedRate : Codable
let currencyName : String
let rate : Double
init(name: String, rate: Double)
self.currencyName = name
self.rate = rate
旁注:当前代码可以根据需要对 JSON 进行解码,但不会按原样重新编码,因为它复制了 Swift 结构:
"base": "EUR",
"date": "2019-05-26",
"rates": [
"currencyName": "BSD",
"rate": 1.118587
,
"currencyName": "BWP",
"rate": 12.040813
,
"currencyName": "BYN",
"rate": 2.3232729999999999
,
"currencyName": "BBD",
"rate": 2.2277930000000001
,
"currencyName": "BOB",
"rate": 7.7293940000000001
,
"currencyName": "BAM",
"rate": 1.9558120000000001
,
"currencyName": "AUD",
"rate": 1.618263
,
"currencyName": "AFN",
"rate": 90.160103000000007
,
"currencyName": "BYR",
"rate": 21964.711670000001
,
"currencyName": "BRL",
"rate": 4.508038
,
"currencyName": "BMD",
"rate": 1.120649
,
"currencyName": "BGN",
"rate": 1.956097
,
"currencyName": "BHD",
"rate": 0.421705
,
"currencyName": "ANG",
"rate": 2.097299
,
"currencyName": "AOA",
"rate": 368.54377299999999
,
"currencyName": "BZD",
"rate": 2.2546889999999999
,
"currencyName": "ARS",
"rate": 50.418429000000003
,
"currencyName": "BTC",
"rate": 0.00013999999999999999
,
"currencyName": "BIF",
"rate": 2051.4592440000001
,
"currencyName": "AWG",
"rate": 2.012124
,
"currencyName": "AED",
"rate": 4.116371
,
"currencyName": "AMD",
"rate": 536.35925399999996
,
"currencyName": "BDT",
"rate": 94.245987999999997
,
"currencyName": "BND",
"rate": 1.5396639999999999
,
"currencyName": "BTN",
"rate": 77.755285999999998
,
"currencyName": "AZN",
"rate": 1.910752
,
"currencyName": "ALL",
"rate": 122.341655
]
【讨论】:
【参考方案2】:您必须编写一个自定义初始化程序,将字典键值对映射到自定义结构的数组。
不必在CurrencyRate
中采用Codable
,因为实例是手动创建的
let jsonString = """
"success": true,
"timestamp": 1558883585,
"base": "EUR",
"date": "2019-05-26",
"rates":
"AED": 4.116371,"AFN": 90.160103,"ALL": 122.341655,"AMD": 536.359254,"ANG": 2.097299,"AOA": 368.543773,"ARS": 50.418429,"AUD": 1.618263,"AWG": 2.012124,"AZN": 1.910752,"BAM": 1.955812,"BBD": 2.227793,"BDT": 94.245988,"BGN": 1.956097,"BHD": 0.421705,"BIF": 2051.459244,"BMD": 1.120649,"BND": 1.539664,"BOB": 7.729394,"BRL": 4.508038,"BSD": 1.118587,"BTC": 0.00014,"BTN": 77.755286,"BWP": 12.040813,"BYN": 2.323273,"BYR": 21964.71167,"BZD": 2.254689
"""
struct ExchangeData : Codable
let timestamp : Date
let base : String
let date : String
let rates : [CurrencyRate]
private enum CodingKeys: String, CodingKey case timestamp, base, date, rates
init(from decoder: Decoder) throws
let container = try decoder.container(keyedBy: CodingKeys.self)
timestamp = try container.decode(Date.self, forKey: .timestamp)
base = try container.decode(String.self, forKey: .base)
date = try container.decode(String.self, forKey: .date)
let rateData = try container.decode([String:Double].self, forKey: .rates)
rates = rateData.map(CurrencyRate.init)
func encode(to encoder: Encoder) throws
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(timestamp, forKey: .timestamp)
try container.encode(base, forKey: .base)
try container.encode(date, forKey: .date)
var rateData = [String:Double]()
rates.forEach rateData[$0.name] = $0.rate
try container.encode(rateData, forKey: .rates)
struct CurrencyRate
let name : String
let rate : Double
let data = Data(jsonString.utf8)
do
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970
let result = try decoder.decode(ExchangeData.self, from: data)
print(result)
catch
print(error)
【讨论】:
以上是关于如何使用 Codable 解码具有更改键的 json 响应?的主要内容,如果未能解决你的问题,请参考以下文章
使用 swift Codable 以值作为键来解码 JSON
如何使用解码器将给定 JSON 中 Double 类型的 Codable 属性转换为 Date?