Swift使用Codable解析JSON时将字符串数值转换为Float类型
解决方案
直接自定义Codable解码逻辑,在解析阶段完成字符串到Float的自动转换,同时兼容空字符串场景,解析完成后数值字段可直接参与计算,无需业务层再做类型转换。
处理规则
- 课程的
grade/weight/credits、作业的score/totalPoints字段类型从String改为Float,如果需要区分「无有效值」和「数值0」,可以改为可选类型Float?,空串/非法格式时解析为nil - 保留原有字段映射关系,不需要调整接口匹配规则
- 空字符串、包含前后空格的字符串、非法数值字符串统一做容错处理,默认返回0(可根据业务需求调整默认值)
改造后完整代码
// MARK: - Welcome struct Welcome: Codable { let currentClasses: [CurrentClass] } // MARK: - CurrentClass struct CurrentClass: Codable { let name: String let grade, weight, credits: Float let lastUpdated: String let assignments: [Assignment] enum CodingKeys: String, CodingKey { case name, grade, weight, credits case lastUpdated = "Last Updated" case assignments } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) name = try container.decode(String.self, forKey: .name) lastUpdated = try container.decode(String.self, forKey: .lastUpdated) assignments = try container.decode([Assignment].self, forKey: .assignments) let stringToFloat: (String) -> Float = { stringValue in let trimmed = stringValue.trimmingCharacters(in: .whitespacesAndNewlines) guard !trimmed.isEmpty else { return 0 } return Float(trimmed) ?? 0 } let gradeStr = try container.decode(String.self, forKey: .grade) grade = stringToFloat(gradeStr) let weightStr = try container.decode(String.self, forKey: .weight) weight = stringToFloat(weightStr) let creditsStr = try container.decode(String.self, forKey: .credits) credits = stringToFloat(creditsStr) } } // MARK: - Assignment struct Assignment: Codable { let assignment: String let category: Category let dateAssigned, dateDue: String let score, totalPoints: Float enum CodingKeys: String, CodingKey { case assignment, category, dateAssigned, dateDue, score, totalPoints } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) assignment = try container.decode(String.self, forKey: .assignment) category = try container.decode(Category.self, forKey: .category) dateAssigned = try container.decode(String.self, forKey: .dateAssigned) dateDue = try container.decode(String.self, forKey: .dateDue) let stringToFloat: (String) -> Float = { stringValue in let trimmed = stringValue.trimmingCharacters(in: .whitespacesAndNewlines) guard !trimmed.isEmpty else { return 0 } return Float(trimmed) ?? 0 } let scoreStr = try container.decode(String.self, forKey: .score) score = stringToFloat(scoreStr) let totalPointsStr = try container.decode(String.self, forKey: .totalPoints) totalPoints = stringToFloat(totalPointsStr) } } enum Category: String, Codable { case majorGrades = "Major Grades" case minorGrades = "Minor Grades" case nonGraded = "Non-graded" }
多场景复用优化
如果项目中存在大量字符串转数值的解析需求,可以抽成属性包装器减少重复代码:
@propertyWrapper struct StringToFloat: Codable { let wrappedValue: Float init(wrappedValue: Float) { self.wrappedValue = wrappedValue } init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let stringValue = try container.decode(String.self) let trimmed = stringValue.trimmingCharacters(in: .whitespacesAndNewlines) wrappedValue = trimmed.isEmpty ? 0 : (Float(trimmed) ?? 0) } }
使用时直接给对应字段添加装饰器即可,无需重写整个模型的初始化方法:
struct CurrentClass: Codable { let name: String @StringToFloat let grade, weight, credits: Float let lastUpdated: String let assignments: [Assignment] enum CodingKeys: String, CodingKey { case name, grade, weight, credits case lastUpdated = "Last Updated" case assignments } }
内容的提问来源于stack exchange,提问作者Abhi
相关产品推荐
相关产品推荐

