Декодирование и преобразование строки в double в codable - PullRequest
0 голосов
/ 11 июля 2020

Как я могу декодировать следующий JSON в объект Codable в Swift?

{"USD":"12.555", "EUR":"11.555"}

Вот структура, которую я использую:

struct Prices: Codable {
    var USD: Double
    var EUR: Double
    
    private enum CodingKeys: String, CodingKey {
        case USD = "USD"
        case EUR = "EUR"
    }
    
    init() {
        self.USD = 0.0
        self.EUR = 0.0
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.USD = try container.decode(Double.self, forKey: .USD)
        self.EUR = try container.decode(Double.self, forKey: .EUR)
    }
}

Ошибка, которую я получение.

Error typeMismatch(Swift.Double, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "USD", intValue: nil)], debugDescription: "Expected to decode Double but found a string/data instead.", underlyingError: nil))

Ответы [ 3 ]

2 голосов
/ 11 июля 2020

Я думаю, что ваша структура неверна и ее будет трудно поддерживать, если вы хотите загрузить больше курсов валют, поэтому я предлагаю другой подход. Сначала создайте структуру для хранения курса валюты

struct CurrencyRate {
    let currency: String
    let rate: Decimal?
}

Затем декодируйте json как словарь и используйте map, чтобы преобразовать его в массив CurrencyRate

var rates = [CurrencyRate]()
do {
    let result = try JSONDecoder().decode([String: String].self, from: json)
    rates = result.map { CurrencyRate(currency: $0.key, rate: Decimal(string: $0.value))}

} catch {
    print(error)
}

Два примечания о CurrencyRate

  • У вас есть две валюты в курсе, поэтому обычно у вас также есть другое свойство с именем baseCurrency или otherCurrency или что-то подобное, но если эта другая валюта всегда одинакова, вы можете опустить ее .
  • В зависимости от вашего варианта использования также может быть хорошей идеей создать новый тип для свойств валюты, Currency
0 голосов
/ 11 июля 2020

Значения, которые вы получаете, являются строками, поэтому ваша структура должна быть просто

struct Prices: Codable {
    let USD: String
    let EUR: String
}

В зависимости от того, как вы собираетесь использовать эти значения цен, преобразование их в двойное может быть нецелесообразным, потому что математика с плавающей запятой странная и Decimal s может быть лучше подходит.

0 голосов
/ 11 июля 2020

Вы должны декодировать значения как String, а затем преобразовать их в Double. Итак, либо приведите String к Double и укажите значение по умолчанию, например:

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.USD = Double(try container.decode(String.self, forKey: .USD)) ?? 0
    self.EUR = Double(try container.decode(String.self, forKey: .EUR)) ?? 0
}

, либо сделайте свойства необязательными и удалите значения по умолчанию:

struct Prices: Codable {
    var USD, EUR: Double?

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.USD = Double(try container.decode(String.self, forKey: .USD))
    self.EUR = Double(try container.decode(String.self, forKey: .EUR))
}

Примечание. : Кроме того, вам не требуется CodingKeys, поскольку имена свойств такие же, как и ключи в данных.

В качестве альтернативы вы можете использовать метод computed property, например:

struct Prices: Codable {
    var USDString: String
    var EURString: String

    private enum CodingKeys: String, CodingKey {
        case USDString = "USD"
        case EURString = "EUR"
    }

    var USD: Double? { Double(USDString) }
    var EUR: Double? { Double(EURString) }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...