New on here and to Swift so please go easy on me..
这里有新的,快的,所以请对我宽容一点。
Am a bit stuck when trying to parse JSON which contains nested dictionaries. I imagine its something wrong with the data strutures I have created and I have tryed everthing to rectify but still getting the same issue.
在解析包含嵌套字典的JSON时遇到了一些困难。我想我创建的数据结构出了问题,我尝试了所有需要纠正的东西,但还是遇到了同样的问题。
This is the JSON api I am trying to work with: https://api.coindesk.com/v1/bpi/currentprice.json
这是我正在尝试使用的JSON api: https://api.coindesk.com/v1/bpi/currentprice.json
These are the data structures I have created to model this:
这些是我创建的数据结构模型:
struct base: Decodable {
let disclaimer: String
let bpi: [Bpi]
}
struct Bpi: Decodable {
let USD: [USD]
}
struct USD: Decodable {
let rate_float: Float
}
And here is my code in the VC :
这是我在VC中的代码:
override func viewDidLoad() {
super.viewDidLoad()
let jsonURLString = "https://api.coindesk.com/v1/bpi/currentprice.json"
guard let url = URL(string: jsonURLString) else {return}
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else {return}
do {
let bitcoinData = try JSONDecoder().decode(base.self, from: data)
print(bitcoinData.bpi)
} catch {
print("error")
}
} .resume() // Fires off the session
}
I can grab the data from the disclaimer string or the other strings in the root dictionary but that is it. I cannot parse anything further with the nested dictonaries - it just throws back the catch error.
我可以从免责字符串或根字典中的其他字符串获取数据,但仅此而已。我无法用嵌套的dictonaries进一步解析任何内容——它只返回捕获错误。
Here is the JSON:
JSON:
{
"time": {
"updated": "Nov 2, 2017 06:08:00 UTC",
"updatedISO": "2017-11-02T06:08:00+00:00",
"updateduk": "Nov 2, 2017 at 06:08 GMT"
},
"disclaimer": "This data was produced from the CoinDesk Bitcoin Price Index (USD). Non-USD currency data converted using hourly conversion rate from openexchangerates.org",
"chartName": "Bitcoin",
"bpi": {
"USD": {
"code": "USD",
"symbol": "$",
"rate": "6,889.4013",
"description": "United States Dollar",
"rate_float": 6889.4013
},
"GBP": {
"code": "GBP",
"symbol": "£",
"rate": "5,184.4053",
"description": "British Pound Sterling",
"rate_float": 5184.4053
},
"EUR": {
"code": "EUR",
"symbol": "€",
"rate": "5,910.4587",
"description": "Euro",
"rate_float": 5910.4587
}
}
}
Is there something I am clearly doing wrong here?
我明显做错了什么吗?
Thanks for the help in advance and sorry if my formatting sucks!
谢谢你的帮助,如果我的格式很糟糕,我很抱歉!
3 个解决方案
#1
1
Try following model, with this it works - both bpi
and USD
are not arrays, just single values:
试试下面的模型,它是有效的——bpi和USD都不是数组,只有一个值:
struct base: Decodable {
let disclaimer: String
let bpi: Bpi
}
struct Bpi: Decodable {
let USD: USD
}
struct USD: Decodable {
let rate_float: Float
}
#2
0
Dictionaries (Dictionary<K,V>
) are implicitly Decodable
compliant if both generic types K
and V
are decodable.
如果泛型类型K和V都是可解码的,则字典(Dictionary
Assuming you create a struct Coin
for the currencies
假设你为这些货币创建了一个struct币。
struct Coin: Decodable {
private enum CodingKeys : String, CodingKey {
case code, symbol, rate, description, rateFloat = "rate_float"
}
let code : String
let symbol : String
let rate : String
let description : String
let rateFloat : Float
}
you can easily decode the currency dictionaries as [String:Coin]
without any additional code
您可以轻松地将货币字典解码为[String:Coin],不需要任何附加代码
struct Base: Decodable {
private enum CodingKeys : String, CodingKey {
case disclaimer, coins = "bpi"
}
let disclaimer: String
let coins: [String:Coin]
}
And use it
并使用它
let bitcoinData = try JSONDecoder().decode(Base.self, from: data)
print(bitcoinData.coins)
Alternatively if you want the currencies as an array of Coin
you can write a custom initializer and map the dictionary values to an array.
或者,如果您希望货币作为一个硬币数组,您可以编写一个自定义初始化器并将字典值映射到一个数组。
This example decodes also the updatedISO
value in the time
dictionary
本例还解码了时间字典中的updatedISO值
struct Base: Decodable {
struct Time : Decodable {
private enum CodingKeys : String, CodingKey {
case updated = "updatedISO"
}
let updated : Date
}
private enum CodingKeys : String, CodingKey {
case disclaimer, bpi, time
}
let disclaimer: String
let coins: [Coin]
let updated : Date
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
disclaimer = try container.decode(String.self, forKey: .disclaimer)
let bpi = try container.decode([String:Coin].self, forKey: .bpi)
coins = Array(bpi.values.sorted(by: {$0.code < $1.code}))
let time = try container.decode(Time.self, forKey: .time)
updated = time.updated
}
}
And use this example
使用这个例子
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let bitcoinData = try decoder.decode(Base.self, from: data)
print(bitcoinData.coins)
#3
0
You declared your bpi & use properties as arrays but they were dictionaries (nested json objects). If you have sample JSON you can try this converter next time: https://danieltmbr.github.io/JsonCodeGenerator/
您声明了bpi &使用属性作为数组,但它们是字典(嵌套的json对象)。如果您有样例JSON,那么下次可以尝试这个转换器:https://danieltmbr.github.io/JsonCodeGenerator/
It generates the following output:
生成如下输出:
struct Root: Codable {
let time: Time
let disclaimer: String
let chartName: String
let bpi: Bpi
}
struct Time: Codable {
let updated: String
let updatedISO: String
let updateduk: String
}
struct Bpi: Codable {
let USD: USD
let GBP: USD
let EUR: USD
}
struct USD: Codable {
let code: String
let symbol: String
let rate: String
let description: String
let rateFloat: Double
private enum CodingKeys: String, CodingKey {
case code
case symbol
case rate
case description
case rateFloat = "rate_float"
}
}
#1
1
Try following model, with this it works - both bpi
and USD
are not arrays, just single values:
试试下面的模型,它是有效的——bpi和USD都不是数组,只有一个值:
struct base: Decodable {
let disclaimer: String
let bpi: Bpi
}
struct Bpi: Decodable {
let USD: USD
}
struct USD: Decodable {
let rate_float: Float
}
#2
0
Dictionaries (Dictionary<K,V>
) are implicitly Decodable
compliant if both generic types K
and V
are decodable.
如果泛型类型K和V都是可解码的,则字典(Dictionary
Assuming you create a struct Coin
for the currencies
假设你为这些货币创建了一个struct币。
struct Coin: Decodable {
private enum CodingKeys : String, CodingKey {
case code, symbol, rate, description, rateFloat = "rate_float"
}
let code : String
let symbol : String
let rate : String
let description : String
let rateFloat : Float
}
you can easily decode the currency dictionaries as [String:Coin]
without any additional code
您可以轻松地将货币字典解码为[String:Coin],不需要任何附加代码
struct Base: Decodable {
private enum CodingKeys : String, CodingKey {
case disclaimer, coins = "bpi"
}
let disclaimer: String
let coins: [String:Coin]
}
And use it
并使用它
let bitcoinData = try JSONDecoder().decode(Base.self, from: data)
print(bitcoinData.coins)
Alternatively if you want the currencies as an array of Coin
you can write a custom initializer and map the dictionary values to an array.
或者,如果您希望货币作为一个硬币数组,您可以编写一个自定义初始化器并将字典值映射到一个数组。
This example decodes also the updatedISO
value in the time
dictionary
本例还解码了时间字典中的updatedISO值
struct Base: Decodable {
struct Time : Decodable {
private enum CodingKeys : String, CodingKey {
case updated = "updatedISO"
}
let updated : Date
}
private enum CodingKeys : String, CodingKey {
case disclaimer, bpi, time
}
let disclaimer: String
let coins: [Coin]
let updated : Date
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
disclaimer = try container.decode(String.self, forKey: .disclaimer)
let bpi = try container.decode([String:Coin].self, forKey: .bpi)
coins = Array(bpi.values.sorted(by: {$0.code < $1.code}))
let time = try container.decode(Time.self, forKey: .time)
updated = time.updated
}
}
And use this example
使用这个例子
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let bitcoinData = try decoder.decode(Base.self, from: data)
print(bitcoinData.coins)
#3
0
You declared your bpi & use properties as arrays but they were dictionaries (nested json objects). If you have sample JSON you can try this converter next time: https://danieltmbr.github.io/JsonCodeGenerator/
您声明了bpi &使用属性作为数组,但它们是字典(嵌套的json对象)。如果您有样例JSON,那么下次可以尝试这个转换器:https://danieltmbr.github.io/JsonCodeGenerator/
It generates the following output:
生成如下输出:
struct Root: Codable {
let time: Time
let disclaimer: String
let chartName: String
let bpi: Bpi
}
struct Time: Codable {
let updated: String
let updatedISO: String
let updateduk: String
}
struct Bpi: Codable {
let USD: USD
let GBP: USD
let EUR: USD
}
struct USD: Codable {
let code: String
let symbol: String
let rate: String
let description: String
let rateFloat: Double
private enum CodingKeys: String, CodingKey {
case code
case symbol
case rate
case description
case rateFloat = "rate_float"
}
}