如何用Swift 4 Codable处理字段值为数组或空字符串的JSON格式不一致问题?
嘿,这种格式不一致的JSON确实挺烦人的,但用Swift的Codable完全能搞定,我来给你分享两个实用的方法!
先假设你的JSON结构大概是这样的:
// 有数据的情况 { "data": [{"id": 1}, {"id": 2}] } // 无数据的情况 { "data": "" }
方法一:手动实现解码初始化方法
这是最直接的方式,在你的Codable结构体里自定义init(from decoder:)方法,手动处理两种情况:
struct MyResponse: Codable { let data: [MyModel] struct MyModel: Codable { let id: Int } enum CodingKeys: String, CodingKey { case data } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) // 先尝试解码成数组 if let dataArray = try? container.decode([MyModel].self, forKey: .data) { self.data = dataArray } else { // 如果解码数组失败,尝试解码成字符串(这里我们只关心空字符串的情况) let emptyString = try container.decode(String.self, forKey: .data) // 不管字符串内容是什么(如果业务允许),都转为空数组 self.data = [] // 如果需要严格校验是空字符串,可以加个判断: // guard emptyString.isEmpty else { // throw DecodingError.dataCorruptedError(forKey: .data, in: container, debugDescription: "Expected empty string or array") // } } } }
这种方法的好处是直接把结果统一成数组类型,后续使用的时候不用再处理额外的包装类型,非常省心。如果业务允许,你甚至可以忽略字符串的内容,直接转为空数组;如果需要严格校验是空字符串,就加上注释里的判断逻辑。
方法二:用枚举包装多类型(更灵活)
如果你需要保留“原始值是数组还是空字符串”的信息,或者有多个字段都是这种格式,可以用一个通用的枚举来处理:
// 定义一个通用枚举,支持数组或空字符串 enum ArrayOrEmptyString<T: Codable>: Codable { case array([T]) case emptyString init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() do { // 先尝试解码数组 let array = try container.decode([T].self) self = .array(array) } catch { // 数组解码失败,尝试解码字符串 let string = try container.decode(String.self) guard string.isEmpty else { // 如果不是空字符串,抛出错误 throw DecodingError.dataCorruptedError(in: container, debugDescription: "Expected empty string or array of \(T.self)") } self = .emptyString } } // 如果需要编码回JSON,可以实现这个方法 func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch self { case .array(let array): try container.encode(array) case .emptyString: try container.encode("") } } } // 在你的响应结构体里使用这个枚举 struct MyResponse: Codable { let data: ArrayOrEmptyString<MyModel> struct MyModel: Codable { let id: Int } }
使用的时候,你可以通过switch来处理两种情况:
switch response.data { case .array(let models): print("拿到数组:\(models)") case .emptyString: print("原始值是空字符串") }
这种方法的优势是通用性强,可以复用在所有类似格式的字段上,而且能保留原始数据的类型信息,适合对数据准确性要求较高的场景。
两种方法都能完美解决你的问题,你可以根据业务需求选择合适的方式~
内容的提问来源于stack exchange,提问作者meaning-matters
相关产品推荐
相关产品推荐

