According my question, I want to decode every fields of my json to string value.
My json look like this
{ name: "admin_tester",
price: 99.89977202,
no: 981,
id: "nfs-998281998",
amount: 98181819911019.828289291329 }
And I want to create my struct like this
struct StockNFS: Decodable {
let name: String?
let price: String?
let no: String?
let id: String?
let amount: String?
}
But If I declare my struct like this, When I use json decode I will get error mismatch type
The reason why I want to mapping every value to string, It is because If I use a double or decimal for price
and amount
, after encode sometime value will incorrect. example 0.125, I wil got 0.124999999.
I just want to recieve any data in string type for just showing on ui ( not edit or manipulate value )
I will appreciate any help. Thank you so much.
CodePudding user response:
To avoid the floating point issues we can either use a type String or Decimal for the keys price and amount. In either case we can not decode directly into either type but we first need to use the given type which is Double so we need a custom init for this.
First case is to use String (I see no reason to use optional fields as default, change this if any of the fields actually can be 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
}
}
The rounding is used with a method inspired from this excellent answer
extension Double {
func roundToDecimal(_ fractionDigits: Int) -> Double {
let multiplier = pow(10, Double(fractionDigits))
return (self * multiplier).rounded() / multiplier
}
}
To do the same but with the numeric type Decimal
we do
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
}
}
Again the rounding method was inspired from the same answer
extension Decimal {
func round(_ mode: Decimal.RoundingMode, precision: Int = 2) -> Decimal {
var result = Decimal()
var value = self
NSDecimalRound(&result, &value, precision, mode)
return result
}
}