Reputation: 3
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 the last couple of days but did not find a solution.
Say, we have the 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?
Upvotes: 0
Views: 825
Reputation: 36264
since your data is not valid JSON due to NaN
(null
is ok), 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)")
}
}
}
}
EDIT-1: alternative using JSContext
:
import JavaScriptCore
struct ContentView: View {
var body: some View {
Text("using JSContext")
.onAppear{
let json = """
[
{
"id": 1,
"apples": 193,
"oranges": NaN,
"bananas": null,
"pineapples": 405,
"watermelons": 13,
"comment": "oranges and bananas have invalid values"
}
]
"""
let fruits = decodeToFruits(json)
print("\n---> fruits: \(fruits)")
}
}
func decodeToFruits(_ badJson: String) -> [Fruits] {
if let goodJson = JSContext().evaluateScript("JSON.stringify(\(badJson))"),
let goodStr = goodJson.toString(),
let data = goodStr.data(using: .utf8) {
do {
return try JSONDecoder().decode([Fruits].self, from: data)
} catch (let error) {
print("\n---> error: \(error)")
}
}
return []
}
}
Upvotes: 2
Reputation: 51
You can set the option of JSONDecoder to try decode not a number value. This option contains case of infinity too.
let decoder = JSONDecoder()
decoder.nonConformingFloatDecodingStrategy = .convertFromString(
positiveInfinity: "Infinity",
negativeInfinity: "-Infinity",
nan: "NaN"
)
Upvotes: 3