Swift 4中的JSON解析帮助——数据结构问题?

时间:2022-08-26 11:28:28

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 )是隐式可解码兼容的。 ,v>

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 )是隐式可解码兼容的。 ,v>

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