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

Time:11-18

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
    }
}
  • Related