Разбор десятичного числа из JSON представлен в виде строки - PullRequest
0 голосов
/ 10 октября 2019

С Xcode 10.2 и iOS 12.x мы смогли извлечь десятичную из строки json. С Xcode 11.1 и iOS 13.1 он генерирует исключение

Ожидается декодировать Double, но вместо этого обнаруживается строка / данные.

class MyClass : Codable {

     var decimal: Decimal?
 }

, затем пытается проанализировать его

let json = "{\"decimal\":\"0.007\"}"
let data = json.data(using: .utf8)
let decoder = JSONDecoder()
decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "s1", negativeInfinity: "s2", nan: "s3")
 do {
   let t = try decoder.decode(MyClass.self, from: data!)
 } catch {
   print(error)
 }

Если я меняю строку json как

let json = "{\"decimal\":0.007}"

Это работает, но опять же мы теряем точность. Есть идеи?

Ответы [ 4 ]

2 голосов
/ 10 октября 2019
struct Root: Codable {
    let decimal: Decimal
}

extension Root {
    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        decimal = try Decimal(string: container.decode(String.self, forKey: .decimal)) ?? .zero
    }
}

let json = #"{"decimal":"0.007"}"# 
do {
    let root = try JSONDecoder().decode(Root.self, from: .init(json.utf8))
    print(root)
} catch {
    print(error)
}

Будет напечатано

Root (десятичное число: 0,007)

1 голос
/ 10 октября 2019

Эта стратегия декодирования не имеет ничего общего с числами, представленными в виде строк. Что вам нужно сделать, это реализовать init(from:) и преобразовать из строки

class MyClass : Codable {
    var decimal: Double?

    enum CodingKeys: String, CodingKey {
        case decimal = "test"
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        decimal = Double(try container.decode(String.self, forKey: .decimal)
        //or if Decimal is used:
        //decimal = Decimal(string: try container.decode(String.self, forKey: .decimal)
    }
}

Обратите внимание, что здесь я использую Double вместо Decimal, чтобы упростить его

0 голосов
/ 10 октября 2019

Тип должен быть Double и определен также при разборе как Double. Свифт разберется с остальными

struct MyClass: Decodable {
        let decimal: Double

        //can be renamed to follow the API name.
        enum CodingKeys: String, CodingKey {
            case decimal
        }
    }
    extension MyClass {
        init(from decoder: Decoder) throws {

            let values = try decoder.container(keyedBy: CodingKeys.self)
            decimal = try values.decode(Double.self, forKey: .decimal)

        }
    }
0 голосов
/ 10 октября 2019

Я считаю, что более чистое решение - это объявить значение не как строку, а как значение:

"test": 0.007

с такой структурой:

struct Stuff {
     var test: Decimal
}

, а затем:

let decoder = JSONDecoder()
let stuff = try decoder.decode(Stuff.self, from: json)

в противном случае вы можете использовать этот пример:

https://forums.swift.org/t/parsing-decimal-values-from-json/6906/3

...