Home > Blockchain >  How to decode NaN value from JSON using Swift?
How to decode NaN value from JSON using Swift?

Time:07-18

Null values decoding works well with Codable protocol, but when I have JSON that has NaN, everything crashes, how do I solve this?

I have spent last couple days, but did not find a solution.

Say, we have following code:

[
{
        "id": 1
        "apples": 193,
        "oranges": NaN,
        "bananas": null,
        "pineapples": 405,
        "watermelons": 13
        "comment": "oranges and bananas have invalid values"
}
]

And this struct:

struct Fruits: Codable, Identifiable {
    var id: Int
    var apples: Int?
    var oranges: Int?
    var bananas: Int?
    var pineapples: Int?
    var watermelons: Int?
    var comment: String?
}

How to decode this with no crashes?

CodePudding user response:

Use decode if present As we know oranges is integer type

import Foundation
struct Fruits : Codable, Identifiable {
    let id : Int?
    let apples : Int?
    let oranges : Int?
    let bananas : Int?
    let pineapples : Int?
    let watermelons : Int?
    let comment : String?

    enum CodingKeys: String, CodingKey {

        case id = "id"
        case apples = "apples"
        case oranges = "oranges"
        case bananas = "bananas"
        case pineapples = "pineapples"
        case watermelons = "watermelons"
        case comment = "comment"
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        id = try values.decodeIfPresent(Int.self, forKey: .id)
        apples = try values.decodeIfPresent(Int.self, forKey: .apples)
        oranges = try values.decodeIfPresent(Int.self, forKey: .oranges)
        bananas = try values.decodeIfPresent(Int.self, forKey: .bananas)
        pineapples = try values.decodeIfPresent(Int.self, forKey: .pineapples)
        watermelons = try values.decodeIfPresent(Int.self, forKey: .watermelons)
        comment = try values.decodeIfPresent(String.self, forKey: .comment)
    }

}

if there is some value against fruits between 0 to n then it will show the value otherwise it will return nil so you can easily achieve

CodePudding user response:

since your data is not valid json, you could try this approach where the original data is made into valid json, works very well for me.

Note: you are also missing a comma after id and watermelons

struct ContentView: View {
    
    var body: some View {
        Text("testing")
            .onAppear{
                let json = """
[
{
        "id": 1,
        "apples": 193,
        "oranges": NaN,
        "bananas": null,
        "pineapples": 405,
        "watermelons": 13,
        "comment": "oranges and bananas have invalid values"
}
]
"""
                // simulated api data
                let data = json.data(using: .utf8)!
                
                // convert to string
                let jsString = String(data: data, encoding: .utf8)!
                // convert back to data after replacements
                let newData = jsString.replacingOccurrences(of: "NaN", with: "null").data(using: .utf8)!
                
                do {
                    let fruits = try JSONDecoder().decode([Fruits].self, from: newData)
                    print("\n---> fruits: \(fruits)")
                } catch (let error) {
                    print("\n---> error: \(error)")
                }
            }
    }
}
  • Related