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

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

【问题讨论】:

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

标签: swift swift4


【解决方案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
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-28
        • 1970-01-01
        相关资源
        最近更新 更多