Swift中如何解析Codable结构体的任意类型rowSet属性?
解决Swift Codable中任意类型嵌套数组的解析问题
你的问题在于rowSet是包含任意类型元素的嵌套数组,而Swift的Codable协议不支持直接使用[Any]或[AnyObject]作为解码类型。下面提供两种可行的解决方案:
方案一:自定义枚举处理JSON基本类型
通过定义一个枚举来覆盖JSON中可能出现的基本类型(字符串、整数、浮点数等),让其遵循Codable协议,以此兼容任意类型的元素。
1. 定义JSONValue枚举
enum JSONValue: Codable { case string(String) case int(Int) case double(Double) case bool(Bool) // 解码逻辑:尝试匹配不同的JSON类型 init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() if let stringVal = try? container.decode(String.self) { self = .string(stringVal) } else if let intVal = try? container.decode(Int.self) { self = .int(intVal) } else if let doubleVal = try? container.decode(Double.self) { self = .double(doubleVal) } else if let boolVal = try? container.decode(Bool.self) { self = .bool(boolVal) } else { throw DecodingError.dataCorruptedError( in: container, debugDescription: "无法解析为支持的JSON基本类型" ) } } // 编码逻辑:根据枚举类型对应编码 func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch self { case .string(let val): try container.encode(val) case .int(let val): try container.encode(val) case .double(let val): try container.encode(val) case .bool(let val): try container.encode(val) } } }
2. 修改ResultSet结构体
注意:你的JSON示例中使用的键是resource,但结构体中定义的是name,需要通过CodingKeys修正映射关系,否则会解码失败。
struct ResultSet: Codable { var name: String var headers: [String] var rowSet: [[JSONValue]] enum CodingKeys: String, CodingKey { case name = "resource" // 修正键名映射 case headers case rowSet } } struct Scoreboard: Codable { var resultSets: [ResultSet] }
使用示例
解码后可以通过switch判断元素类型:
let jsonData = // 你的JSON数据 do { let scoreboard = try JSONDecoder().decode(Scoreboard.self, from: jsonData) for resultSet in scoreboard.resultSets { print("资源名称:\(resultSet.name)") for row in resultSet.rowSet { for element in row { switch element { case .string(let str): print("字符串元素:\(str)") case .int(let num): print("整数元素:\(num)") case .double(let num): print("浮点数元素:\(num)") case .bool(let flag): print("布尔元素:\(flag)") } } } } } catch { print("解码失败:\(error)") }
方案二:实现通用AnyCodable类型
如果需要支持更复杂的JSON结构(比如嵌套对象、数组),可以实现一个AnyCodable结构体来容纳任意JSON类型:
1. 定义AnyCodable
struct AnyCodable: Codable { let value: Any init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() if let val = try? container.decode(String.self) { value = val } else if let val = try? container.decode(Int.self) { value = val } else if let val = try? container.decode(Double.self) { value = val } else if let val = try? container.decode(Bool.self) { value = val } else if let val = try? container.decode([AnyCodable].self) { value = val.map { $0.value } } else if let val = try? container.decode([String: AnyCodable].self) { value = val.mapValues { $0.value } } else { throw DecodingError.dataCorruptedError( in: container, debugDescription: "不支持的JSON类型" ) } } func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch value { case let val as String: try container.encode(val) case let val as Int: try container.encode(val) case let val as Double: try container.encode(val) case let val as Bool: try container.encode(val) case let val as [Any]: try container.encode(val.map(AnyCodable.init)) case let val as [String: Any]: try container.encode(val.mapValues(AnyCodable.init)) default: throw EncodingError.invalidValue( value, EncodingError.Context( codingPath: container.codingPath, debugDescription: "无法编码该类型" ) ) } } }
2. 修改ResultSet结构体
同样需要修正键名映射:
struct ResultSet: Codable { var name: String var headers: [String] var rowSet: [[AnyCodable]] enum CodingKeys: String, CodingKey { case name = "resource" case headers case rowSet } } struct Scoreboard: Codable { var resultSets: [ResultSet] }
使用示例
解码后通过value属性获取原始值,并进行类型判断:
do { let scoreboard = try JSONDecoder().decode(Scoreboard.self, from: jsonData) for resultSet in scoreboard.resultSets { for row in resultSet.rowSet { for element in row { switch element.value { case let str as String: print("字符串:\(str)") case let num as Int: print("整数:\(num)") case let num as Double: print("浮点数:\(num)") case let flag as Bool: print("布尔值:\(flag)") case let arr as [Any]: print("数组:\(arr)") case let dict as [String: Any]: print("字典:\(dict)") default: print("未知类型") } } } } } catch { print("解码失败:\(error)") }
内容的提问来源于stack exchange,提问作者Omar Ebrahim
相关产品推荐
相关产品推荐

