使 NSDecimalNumber 可编码

Posted

技术标签:

【中文标题】使 NSDecimalNumber 可编码【英文标题】:Making NSDecimalNumber Codable 【发布时间】:2017-09-19 07:43:59 【问题描述】:

是否可以扩展 NSDecimalNumber 以符合 Encodable & Decodable 协议?

【问题讨论】:

到目前为止你尝试过什么?你有什么要求?你想编码/解码什么? @LorenzoB 我检查了 Apple 的文档 developer.apple.com/documentation/foundation/… 以了解对自定义类型的编码和解码。我正在尝试解析来自服务器的响应,如果我使用 Double,我会失去精度。 【参考方案1】:

无法扩展NSDecimalNumber 以符合EncodableDecodable 协议。 Jordan Rose 在下面的swift evolution email thread 中对此进行了解释。

如果您需要在 API 中输入 NSDecimalValue,您可以围绕 Decimal 构建计算属性。

struct YourType: Codable 
    var decimalNumber: NSDecimalNumber 
        get  return NSDecimalNumber(decimal: decimalValue) 
        set  decimalValue = newValue.decimalValue 
    
    private var decimalValue: Decimal

顺便说一句。如果您使用NSNumberFormatter 进行解析,请注意known bug 在某些情况下会导致精度损失。

let f = NumberFormatter()
f.generatesDecimalNumbers = true
f.locale = Locale(identifier: "en_US_POSIX")
let z = f.number(from: "8.3")!
// z.decimalValue._exponent is not -1
// z.decimalValue._mantissa is not (83, 0, 0, 0, 0, 0, 0, 0)

改为以这种方式解析字符串:

NSDecimalNumber(string: "8.3", locale: Locale(identifier: "en_US_POSIX"))

【讨论】:

【参考方案2】:

在swift中你应该使用Decimal类型。此类型从框中确认协议Encodable & Decodable

如果您在代码中输入了NSDecimalNumber,则很容易将其转换为Decimal

let objcDecimal = NSDecimalNumber(decimal: 10)
let swiftDecimal = (objcDecimal as Decimal)

【讨论】:

【参考方案3】:

使用 Swift 5.1,您可以使用属性包装器来避免编写自定义 init(from decoder: Decoder) / encode(to encoder: Encoder) 的样板。

@propertyWrapper
struct NumberString 
    private let value: String
    var wrappedValue: NSDecimalNumber

    init(wrappedValue: NSDecimalNumber) 
        self.wrappedValue = wrappedValue
        value = wrappedValue.stringValue
    


extension NumberString: Decodable 
    init(from decoder: Decoder) throws 
        value = try String(from: decoder)
        wrappedValue = NSDecimalNumber(string: value)
    


extension NumberString: Encodable 
    func encode(to encoder: Encoder) throws 
        var container = encoder.singleValueContainer()
        try container.encode(wrappedValue.stringValue)
    


extension NumberString: Equatable 

用法:

struct Foo: Codable 
    @NumberString var value: NSDecimalNumber

【讨论】:

以上是关于使 NSDecimalNumber 可编码的主要内容,如果未能解决你的问题,请参考以下文章

找不到在 Vapor 3 中使连接查询结果可编码的方法

可编码为 CKRecord

如何在目标 c 数据模型类中使用 Codable 协议?

为啥 Firestore 可编码支持不适用于此示例

使用 JSONDecoder 和可编码协议的网络

如何在“可编码”响应中使用字典?