Swift解析JSON时无法将1.1转换为Float的问题
Swift JSON解析问题:1.1无法直接转为Float,其他小数正常
问题描述
在Swift中解析JSON时遇到异常:JSON中的数值1.1无法直接通过as? Float转换成功,返回nil;但1.0、1.25、1.5这类小数却能正常转换。尝试先将数值转为字符串再转Float可以成功,需要明确直接转换失败的原因。
相关代码
JSON解析代码
if let path = Bundle.main.path(forResource: "moves", ofType: "json") { do { let data = try Data(contentsOf: URL(fileURLWithPath: path)) let json = try JSONSerialization.jsonObject(with: data, options: []) if let array = json as? [[String:Any]] { for dict in array { if let actionName = dict["name"] as? String, let type = dict["type"] as? String, let amt = dict["amtlost"] as? Int, let val = dict["atkdefval"] as? Float, let dtype = dict["element"] as? String, let target = dict["target"] as? Bool, let steal = dict["stealAmt"] as? Float{ let cmove = Actions(name: actionName, type: type, amtlost: amt, atkdefval: val, damageType: dtype, target: target, stealAmt: steal) self.moves.append(cmove) } else { print(dict) let val = dict["atkdefval"] as? Float print(val) } } } } catch { print("Error reading location JSON file: \(error)") } } else { print("Could not read JSON") } print(self.moves.count) for move in moves { print(move.name) print(move.atkdefval) }
对应JSON数据
[ { "name": "Slash", "type": "phys", "amtlost": 0, "atkdefval": 1.5, "element": "slashing", "target": false, "stealAmt": 0 }, { "name": "Punch", "type": "phys", "amtlost": 0, "atkdefval": 1.1, "element": "bludgeoning", "target": false, "stealAmt": 0 }, { "name": "Magic Missile", "type": "magic", "amtlost": 2, "atkdefval": 1.75, "element": "force", "target": false, "stealAmt": 0 }, { "name": "Block", "type": "defense", "amtlost": 0, "atkdefval": 1.5, "element": "bludgeoning", "target": false, "stealAmt": 0 }, { "name": "Healing Word", "type": "healing", "amtlost": 2, "atkdefval": 1.25, "element": "light", "target": false, "stealAmt": 0 }, { "name": "Shield", "type": "shield", "amtlost": 1, "atkdefval": 1.5, "element": "force", "target": false, "stealAmt": 0 } ]
Actions类代码
enum ActionType:String, Codable{ case phys = "phys" case magic = "magic" case defense = "defense" case healing = "healing" case shield = "shield" } enum DamageElement: String, Codable{ case slash = "slashing" case pierce = "piercing" case bludgeon = "bludgeoning" case fire = "fire" case ice = "ice" case earth = "earth" case lightning = "lightning" case light = "light" case dark = "dark" case force = "force" } class Actions: Codable, Hashable, Equatable{ var name: String var type: ActionType var amtlost: Int var atkdefval: Float var element: DamageElement var target: Bool // false is hp, true is mp var stealAmt: Float static func == (lhs: Actions, rhs: Actions) -> Bool { return lhs.name == rhs.name } func hash(into hasher: inout Hasher) { return hasher.combine(ObjectIdentifier(self))} init(){ self.name = "punch" self.type = .phys self.amtlost = 0 self.atkdefval = 4 self.element = .bludgeon self.target = false self.stealAmt = 0 } init(name: String, type: String, amtlost: Int, atkdefval: Float, damageType: String, target: Bool, stealAmt: Float) { self.name = name self.type = ActionType(rawValue: type.lowercased())! self.amtlost = amtlost self.atkdefval = atkdefval self.element = DamageElement(rawValue: damageType.lowercased())! self.target = target self.stealAmt = stealAmt } }
原因分析
- JSONSerialization的数值存储类型:
JSONSerialization解析JSON中的小数时,会统一将其转换为Double类型存储,而非Float。 - 二进制浮点数精度差异:
- 像1.5、1.25这类数可以用二进制浮点数精确表示,对应的
Double值转Float是无损的,因此as? Float能成功匹配类型并转换。 - 1.1无法用二进制浮点数精确表示,存储为
Double时是一个近似值。Swift的as?类型转换是严格的类型检查,不会自动执行数值类型的隐式转换,所以将Double实例直接强转为Float会返回nil。
- 像1.5、1.25这类数可以用二进制浮点数精确表示,对应的
- 字符串中转的原理:先转字符串再转Float,本质是通过字符串重新解析数值,这个过程会做近似处理,从而得到对应的Float值。
解决方案
方案1:显式转换Double为Float
将解析Float的代码改为先获取Double值,再显式转换为Float:
let val = (dict["atkdefval"] as? Double).map { Float($0) } let steal = (dict["stealAmt"] as? Double).map { Float($0) }
方案2:使用Codable自动解析(推荐)
既然Actions类已经实现了Codable,可以直接用JSONDecoder完成解析,无需手动处理字典,代码更简洁且自动处理数值类型转换:
if let path = Bundle.main.path(forResource: "moves", ofType: "json") { do { let data = try Data(contentsOf: URL(fileURLWithPath: path)) self.moves = try JSONDecoder().decode([Actions].self, from: data) } catch { print("Error parsing JSON: \(error)") } } else { print("Could not read JSON") }
注:确保JSON字段名与
Actions类的属性名一致,Codable会自动完成映射;若字段名不一致,可通过CodingKeys枚举自定义映射关系。
内容的提问来源于stack exchange,提问作者Andrew Nazareth
相关产品推荐
相关产品推荐

