首页 > 解决方案 > 从呈现为字符串的 JSON 解析十进制

问题描述

使用 Xcode 10.2 和 iOS 12.x,我们能够从 json 字符串中提取 Decimal。对于 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}"

它有效,但我们又一次失去了精确度。有任何想法吗?

标签: swiftios13codable

解决方案


您需要扩展 KeyedDecodingContainer 并为 Decimal.Type 添加一个实现。

extension KeyedDecodingContainer {
    func decode(_ type: Decimal.Type, forKey key: K) throws -> Decimal {
        let stringValue = try decode(String.self, forKey: key)
        guard let decimalValue = Decimal(string: stringValue) else {
            let context = DecodingError.Context(codingPath: [key], debugDescription: "The key \(key) couldn't be converted to a Decimal value")
            throw DecodingError.typeMismatch(type, context)
        }
        return decimalValue
    }
}

这是一个例子:

let json = """
{
  "capAmount": "123.45"
}
"""

struct Status: Decodable {
    let capAmount: Decimal

    enum CodingKeys: String, CodingKey {
        case capAmount
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        capAmount = try container.decode(Decimal.self, forKey: .capAmount)
    }
}

// Execute it
if let data = json.data(using: .utf8){
    let status = try JSONDecoder().decode(Status.self, from: data)
    print(status.capAmount)
}

推荐阅读