如何使用 Swift 中的 Decoadable 对象将任何 json 值解码为字符串?

How to decode any json value to string with Decoadable object in Swift?

根据我的问题,我想将 json 的每个字段解码为字符串值。

我的json长这样

{ name: "admin_tester",
  price: 99.89977202, 
  no: 981,
  id: "nfs-998281998",
  amount: 98181819911019.828289291329 }

我想像这样创建我的结构

struct StockNFS: Decodable {
     let name: String?
     let price: String?
     let no: String?
     let id: String?
     let amount: String?
}

但是如果我这样声明我的结构,当我使用 json 解码时我会得到错误不匹配类型

之所以要将每个值映射到字符串,是因为如果我对 priceamount 使用双精度或小数,编码后有时值会不正确。例如0.125,我会得到0.124999999.

我只想接收字符串类型的任何数据,以便仅在 ui 上显示(不编辑或操作值)

如有任何帮助,我将不胜感激。非常感谢。

为避免浮点数问题,我们可以为键的价格和金额使用字符串或小数类型。在任何一种情况下,我们都不能直接解码为任何一种类型,但我们首先需要使用给定的类型,即 Double,因此我们需要为此自定义初始化。

第一种情况是使用 String(我认为没有理由将可选字段用作默认值,如果任何字段实际上可以为 nil,请更改此设置)

struct StockNFS: Codable {
    let name: String
    let price: String
    let no: Int
    let id: String
    let amount: String

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        name = try container.decode(String.self, forKey: .name)
        let priceValue = try container.decode(Double.self, forKey: .price)
        price = "\(priceValue.roundToDecimal(8))"
        //... rest of the values
    }
}

四舍五入的方法灵感来自this优秀答案

 extension Double {
    func roundToDecimal(_ fractionDigits: Int) -> Double {
        let multiplier = pow(10, Double(fractionDigits))
        return (self * multiplier).rounded() / multiplier
    }
}

做同样的事情,但使用数字类型Decimal我们做

struct StockNFS2: Codable {
    let name: String
    let price: Decimal
    let no: Int
    let id: String
    let amount: Decimal
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        name = try container.decode(String.self, forKey: .name)
        let priceValue = try container.decode(Double.self, forKey: .price)
        price = Decimal(priceValue).round(.plain, precision: 8)
        //... rest of the values
    }
}

舍入方法再次受到同一个答案的启发

extension Decimal {
    func round(_ mode: Decimal.RoundingMode, precision: Int = 2) -> Decimal {
        var result = Decimal()
        var value = self
        NSDecimalRound(&result, &value, precision, mode)
        return result
    }
}