Using Swift 5.1, you may choose one of the three following ways in order to solve your problem.
#1. Using Decodable
init(from:)
initializer
Use this strategy when you need to convert from String
to Float
for a single struct, enum or class.
import Foundation
struct ExampleJson: Decodable {
var name: String
var age: Int
var taxRate: Float
enum CodingKeys: String, CodingKey {
case name, age, taxRate = "tax_rate"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: CodingKeys.name)
age = try container.decode(Int.self, forKey: CodingKeys.age)
let taxRateString = try container.decode(String.self, forKey: CodingKeys.taxRate)
guard let taxRateFloat = Float(taxRateString) else {
let context = DecodingError.Context(codingPath: container.codingPath + [CodingKeys.taxRate], debugDescription: "Could not parse json key to a Float object")
throw DecodingError.dataCorrupted(context)
}
taxRate = taxRateFloat
}
}
Usage:
import Foundation
let jsonString = """
{
"name": "Bob",
"age": 25,
"tax_rate": "4.25"
}
"""
let data = jsonString.data(using: String.Encoding.utf8)!
let decoder = JSONDecoder()
let exampleJson = try! decoder.decode(ExampleJson.self, from: data)
dump(exampleJson)
/*
prints:
▿ __lldb_expr_126.ExampleJson
- name: "Bob"
- age: 25
- taxRate: 4.25
*/
#2. Using an intermediate model
Use this strategy when you have many nested keys in your JSON or when you need to convert many keys (e.g. from String
to Float
) from your JSON.
import Foundation
fileprivate struct PrivateExampleJson: Decodable {
var name: String
var age: Int
var taxRate: String
enum CodingKeys: String, CodingKey {
case name, age, taxRate = "tax_rate"
}
}
struct ExampleJson: Decodable {
var name: String
var age: Int
var taxRate: Float
init(from decoder: Decoder) throws {
let privateExampleJson = try PrivateExampleJson(from: decoder)
name = privateExampleJson.name
age = privateExampleJson.age
guard let convertedTaxRate = Float(privateExampleJson.taxRate) else {
let context = DecodingError.Context(codingPath: [], debugDescription: "Could not parse json key to a Float object")
throw DecodingError.dataCorrupted(context)
}
taxRate = convertedTaxRate
}
}
Usage:
import Foundation
let jsonString = """
{
"name": "Bob",
"age": 25,
"tax_rate": "4.25"
}
"""
let data = jsonString.data(using: String.Encoding.utf8)!
let decoder = JSONDecoder()
let exampleJson = try! decoder.decode(ExampleJson.self, from: data)
dump(exampleJson)
/*
prints:
▿ __lldb_expr_126.ExampleJson
- name: "Bob"
- age: 25
- taxRate: 4.25
*/
#3. Using a KeyedDecodingContainer
extension method
Use this strategy when converting from some JSON keys' types to your model's property types (e.g. String
to Float
) is a common pattern in your application.
import Foundation
extension KeyedDecodingContainer {
func decode(_ type: Float.Type, forKey key: Key) throws -> Float {
if let stringValue = try? self.decode(String.self, forKey: key) {
guard let floatValue = Float(stringValue) else {
let context = DecodingError.Context(codingPath: codingPath, debugDescription: "Could not parse json key to a Float object")
throw DecodingError.dataCorrupted(context)
}
return floatValue
} else {
let doubleValue = try self.decode(Double.self, forKey: key)
return Float(doubleValue)
}
}
}
struct ExampleJson: Decodable {
var name: String
var age: Int
var taxRate: Float
enum CodingKeys: String, CodingKey {
case name, age, taxRate = "tax_rate"
}
}
Usage:
import Foundation
let jsonString = """
{
"name": "Bob",
"age": 25,
"tax_rate": "4.25"
}
"""
let data = jsonString.data(using: String.Encoding.utf8)!
let decoder = JSONDecoder()
let exampleJson = try! decoder.decode(ExampleJson.self, from: data)
dump(exampleJson)
/*
prints:
▿ __lldb_expr_126.ExampleJson
- name: "Bob"
- age: 25
- taxRate: 4.25
*/