How to retrieve a value from JSON Object Swift 4

2019-08-21 06:54发布

问题:

I am trying to decode JSON object using Codable with Swift 4:

{
USD: {
"15m": 9977.49,
last: 9977.49,
buy: 9979.36,
sell: 9975.62,
symbol: "$"
    },
AUD: {
"15m": 13181.69,
last: 13181.69,
buy: 13184.16,
sell: 13179.22,
symbol: "$"
    },
TBD: {
"15m": 13181.69,
last: 13181.69,
buy: 13184.16,
sell: 13179.22,
symbol: "$"
    }
}

This is what I've done so far with a model of an object:

struct Currency: Codable {

    let fifteenMin: Double?
    let last: Double?
    let buy: Double
    let sell: Double
    let symbol: String

    enum CodingKeys: String, CodingKey {
        case fifteenMin = "15m"
        case last
        case buy
        case sell
        case symbol
    }
}

And this is how I decode that:

var currencyName = [String]()
var currenciesArray = [Currency]()


func fetchCurrencyData() {
    guard let urlString = API.RateURL.absoluteString else { return }
    guard let url = URL(string: urlString) else { return }
    let jsonData = try! Data(contentsOf: url)
    let decoder = JSONDecoder()
    do {
        let currenciesData = try decoder.decode([String: Currency].self, from: jsonData)
        for currency in currenciesData {
            self.currencyName.append(currency.key)
            self.currenciesArray.append(currency.value)
        }
    } catch let err {
        print(err.localizedDescription)
    }
}

So when I want to populate rows in tableView with that Data I use "for loop" and append "USD", "AUD" and so on in one array and Currency object in another array.

What is the better way of fetching data and populating Currency object. Cuz I am pretty sure that fetching and appending the same object of JSON into two separate arrays isn't good practice I assume.

If my question isn't clear I can explain in more details what I would like to achieve.

Thank you.

回答1:

It's quite annoying (and error-prone) to maintain multiple arrays. I recommend to include the name in the struct.

Add this line in Currency

var name = ""

After decoding the JSON sort the keys, get the corresponding value and set the name to the key

var currenciesArray = [Currency]()

let currencies = try decoder.decode([String: Currency].self, from: jsonData)

for key in currencies.keys.sorted() {
    var currency = currencies[key]!
    currency.name = key
    currenciesArray.append(currency)
}
print(currenciesArray)


回答2:

Shouldn't the currency name be inside the Currency object? That would be much easier to manipulate.

But if you must use that format, you can create an array of tuples or dictionaries.

var currenciesArray = [(String, Currency)]() 

and then append like this:

for currency in currenciesData {
    currenciesArray.append((currency.key, currency.value))
}

p.s. : You need to add quotes(") on the string values inside the JSON, or it will be invalid.

****Added*****

The code below will encode and decode a JSON similar to the one you mentioned:

struct Price: Codable {

    let fifteenMin: Double?
    let last: Double?
    let buy: Double
    let sell: Double
    let symbol: String

    enum CodingKeys: String, CodingKey {
        case fifteenMin = "15m"
        case last
        case buy
        case sell
        case symbol
    }
}

struct Currencies: Codable {
    var USD: Price? 
    var JPY: Price?
    var EUR: Price?
    var AUD: Price?
}

let usdPrice = Price(fifteenMin: 10, last: 11, buy: 12, sell: 13, symbol: "$")
let jpyPrice = Price(fifteenMin: 10, last: 11, buy: 12, sell: 13, symbol: "¥")
var currencies = Currencies()
currencies.USD = usdPrice


// Encode data
let jsonEncoder = JSONEncoder()
do {
    let jsonData = try jsonEncoder.encode(currencies)
    let jsonString = String(data: jsonData, encoding: .utf8)
    print("JSON String : " + jsonString!)
    do {
        // Decode data to object
        let jsonDecoder = JSONDecoder()
        let currencies = try jsonDecoder.decode(Currencies.self, from: jsonData)

        print("USD: \(currencies.USD)")
        print("JPY: \(currencies.JPY)")
    }
    catch {
    }

}
catch {
}