Swift - 如何提取具有相同键名的所有值

Swift - How to extract all values with same key names

看了几个 post 我没有找到我要找的东西。我希望这个 post 能帮到我。

我用的是 CoinDesk 的 api,我现在想做的是在答案中检索所有代码(欧元、美元、英镑),但我无法获取所有资产。

    {
  "time": {
    "updated": "Feb 20, 2021 19:48:00 UTC",
    "updatedISO": "2021-02-20T19:48:00+00:00",
    "updateduk": "Feb 20, 2021 at 19:48 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": "57,014.5954",
      "description": "United States Dollar",
      "rate_float": 57014.5954
    },
    "GBP": {
      "code": "GBP",
      "symbol": "£",
      "rate": "40,681.1111",
      "description": "British Pound Sterling",
      "rate_float": 40681.1111
    },
    "EUR": {
      "code": "EUR",
      "symbol": "€",
      "rate": "47,048.5582",
      "description": "Euro",
      "rate_float": 47048.5582
    }
  }
}

这是我获取数据的方式

public class NetworkManager {

static public func fetchBPI() {
    let url = "https://api.coindesk.com/v1/bpi/currentprice.json"
    Alamofire.request(url).responseJSON { response in
        switch response.result {
        case .success:
            print("✅ Success ✅")
            if let json = response.data {
                do {
                    let data = try JSON(data: json)
                    print(data)
                    
                    let context = PersistentContainer.context
                    let entity = NSEntityDescription.entity(forEntityName: "BPI", in: context)
                    let newObject = NSManagedObject(entity: entity!, insertInto: context)
                    
                    //Date Formatter
                    let dateFormatter = DateFormatter()
                    dateFormatter.dateFormat = "MM-dd-yyyy HH:mm"
                    let date = dateFormatter.date(from: data["time"]["updated"].rawValue as! String)
                    dateFormatter.timeZone = NSTimeZone.local
                    let timeStamp = dateFormatter.string(from: date ?? Date())
                    
                    newObject.setValue(timeStamp, forKey: "time")
                    newObject.setValue(data["chartName"].rawValue, forKey: "chartName")
                    newObject.setValue(data["bpi"]["EUR"]["symbol"].rawValue, forKey: "symbol")
                    newObject.setValue(data["bpi"]["EUR"]["rate"].rawValue, forKey: "rate")
                    newObject.setValue(data["bpi"]["EUR"]["code"].rawValue, forKey: "code")
                    
                    do {
                        try context.save()
                        print("✅ Data saved ✅")
                    } catch let error {
                        print(error)
                        print("❌ Saving Failed ❌")
                    }
                }
                catch {
                    print("❌ Error ❌")
                }
            }
        case .failure(let error):
            print(error)
        }
    }
}

}

我想把所有的代码都输入到bpi密钥中,然后把它们放在一个列表中使用。

您应该使用对象来表示来自服务器的数据。它会是这样的:

struct CoinData: Codable {
    let time: Time
    let disclaimer, chartName: String
    let bpi: BPI
}

struct BPI: Codable {
    let usd, gbp, eur: Eur

    enum CodingKeys: String, CodingKey {
        case usd = "USD"
        case gbp = "GBP"
        case eur = "EUR"
    }
}

struct Eur: Codable {
    let code, symbol, rate, eurDescription: String
    let rateFloat: Double

    enum CodingKeys: String, CodingKey {
        case code, symbol, rate
        case eurDescription = "description"
        case rateFloat = "rate_float"
    }
}

struct Time: Codable {
    let updated: String
    let updatedISO: String
    let updateduk: String
}

当你下载数据时,你会像这样解析它:

let coinData = try? JSONDecoder().decode(CoinData.self, from: jsonData)
coinData?.bpi.eur  // Access EUR for instance

更新:

使用从服务器获取的数据演示解析的简单演示:

let dataFromServer = "{\"time\":{\"updated\":\"Feb 20, 2021 21:02:00 UTC\",\"updatedISO\":\"2021-02-20T21:02:00+00:00\",\"updateduk\":\"Feb 20, 2021 at 21:02 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\":\"56,689.8367\",\"description\":\"United States Dollar\",\"rate_float\":56689.8367},\"GBP\":{\"code\":\"GBP\",\"symbol\":\"£\",\"rate\":\"40,449.3889\",\"description\":\"British Pound Sterling\",\"rate_float\":40449.3889},\"EUR\":{\"code\":\"EUR\",\"symbol\":\"€\",\"rate\":\"46,780.5666\",\"description\":\"Euro\",\"rate_float\":46780.5666}}}"

do {
    let json = try JSONDecoder().decode(CoinData.self, from: dataFromServer.data(using: .utf8)!)
    print(json.bpi.eur)  // Will print EUR object
} catch let error {
    print(error)
}

我认为这里处理json的最好方法是将“bpi”下的内容当作字典来处理。

struct CoinData: Codable {
    let time: Time
    let chartName: String
    let bpi: [String: BPI]
}

struct BPI: Codable {
    let code: String
    let rate: Double

    enum CodingKeys: String, CodingKey {
        case code
        case rate = "rate_float"
    }
}

struct Time: Codable {
    let updated: Date

    enum CodingKeys: String, CodingKey {
        case updated = "updatedISO"
    }
}

我删除了一些不必要的 (?) 属性,还注意到我将 Time 中的 updatedISO 变成了 Date 如果这可能有用,因为它很容易转换.

要正确解码此代码,请使用 trydo/catch,以便正确处理错误。

这是一个例子,我也循环了不同的 currencies/rates

do {
    let decoder = JSONDecoder()
    decoder.dateDecodingStrategy = .iso8601
    let result = try decoder.decode(CoinData.self, from: data)

    print(result.time.updated)
    let coins = result.bpi.values
    for coin in coins {
        print(coin)
    }
} catch {
    print(error)
}

输出:

2021-02-20 19:48:00 +0000
BPI(code: "GBP", rate: 40681.1111)
BPI(code: "USD", rate: 57014.5954)
BPI(code: "EUR", rate: 47048.5582)