将字符串值编码为Double - Swift



我有一个Django后端发送pythondecimal值作为字符串(精度)。我试图编码decimal数据到Double在我的Swift应用程序。

这是我的结构体:

public struct MyStruct: Encodable, Decodable {

public var amount: Double?
public init(amount: Double? = nil) {
self.amount = amount
}
}

可能的json:

{
"amount" = null
}

或:

{
"amount" = "3187.498149184"
}

当我尝试编码我的结构时,我得到这个错误:

期望解码Double,但发现一个字符串/data。

最简单的方法是什么?我有很多结构模型包含双值,所以我正在寻找一个简单的解决方案。

将您的精确值包装到具有自定义解码的自定义struct中:

public struct PreciseNumber: Codable {
public let value: Decimal
private static let posixLocale = Locale(identifier: "en_US_POSIX")
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let rawValue = try container.decode(String.self)
value = NSDecimalNumber(string: rawValue, locale: Self.posixLocale) as Decimal
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode((value as NSDecimalNumber).description(withLocale: Self.posixLocale))
}
}

则直接解码PreciseNumber:

public var amount: PreciseNumber?

我故意使用Decimal而不是Double,因为Double会失去精度。

一个稍微复杂一点的解决方案是使用属性包装器:

@propertyWrapper
public struct NumberAsString: Codable {
public var wrappedValue: Decimal?
private static let posixLocale = Locale(identifier: "en_US_POSIX")
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let rawValue = try container.decode(String.self)

guard !container.decodeNil() else {
wrappedValue = nil
return
}
wrappedValue = NSDecimalNumber(string: rawValue, locale: Self.posixLocale) as Decimal
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
guard let wrappedValue = wrappedValue else {
try container.encodeNil()
return
}
try container.encode((wrappedValue as NSDecimalNumber).description(withLocale: Self.posixLocale))
}
public init(wrappedValue: Decimal?) {
self.wrappedValue = wrappedValue
}
}

用作:

public struct MyStruct: Codable {
@NumberAsString public var amount: Decimal?
public init(amount: Decimal? = nil) {
self.amount = amount
}
}

只需将您的属性类型更改为string并添加自定义getter以获得Double。

public struct MyStruct: Encodable, Decodable {

private var amount: String?
public var getAmount: Double? {
if let amount = amount {
return Double(amount)
}
return nil
}
public init(amount: String? = nil) {
self.amount = amount
}
}

public struct Amount: Codable {
public let value: String?

public var toDouble: Double? {
if let val = value { return Double(val)}
return nil
}

public init(amount: Double? = nil) {
if let val = amount {
self.value = String(val)
} else {
self.value = nil
}
}

public init(from decoder: Decoder) throws {
value = try decoder.singleValueContainer().decode(String.self)
}

public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(value)
}
}
public struct MyStruct: Encodable, Decodable {

public var amount: Amount?
public init(amount: Double? = nil) {
self.amount = Amount(amount: amount)
}
}
let JSONString = """
{
"amount": "3187.498149184",
}
"""

do {
let result = try JSONDecoder().decode(MyStruct.self, from: Data(JSONString.utf8))
print(result.amount?.value)
}
catch let error {
print(error)
}

最新更新