如何让Array和Dictionary类型递归遵循自定义JSONType协议?
问题根本原因
- Swift默认不支持协议存在类型(也就是你写的
JSONType/Encodable这种直接当类型用的协议)自动遵循该协议本身。你声明extension Array: JSONType where Element: JSONType时,[JSONType]的元素类型是JSONType协议存在类型,它没有遵循JSONType协议,因此不满足扩展约束,无法判定[JSONType]自身遵循JSONType,就会抛出编译错误。 - 你修改为
Element == JSONType的约束只能支持一层集合嵌套,当出现[[String: JSONType]]这种多层嵌套时,内层数组的元素是[String: JSONType]类型,不等于JSONType存在类型,因此又会触发约束不匹配的报错。
可行解决方案
方案1:Swift 5.7+ 极简适配(推荐,仅需加1行代码)
Swift 5.7引入了显式声明协议存在类型遵循自身的语法,只需要在你的协议定义后加一行扩展即可解决所有报错:
public protocol JSONType {} // 新增这行,明确声明JSONType协议的存在类型自身遵循JSONType extension JSONType: JSONType {} extension String: JSONType {} extension Int: JSONType {} extension Double: JSONType {} extension Bool: JSONType {} extension NSNull: JSONType {} extension Array: JSONType where Element: JSONType {} extension Dictionary: JSONType where Key == String, Value: JSONType {} // 你的原有代码全部可以正常编译 func serializeToJSON(_ object: JSONType) -> Data { return try! JSONSerialization.data(withJSONObject: object) } var array1: [JSONType] = [""] var array2: [JSONType] = [""] array1[0] = array2 // 编译正常 var dict = [String: JSONType]() dict[""] = [[String: JSONType]]() // 编译正常
方案2:枚举封装JSON值(兼容所有Swift版本,类型安全最高)
如果你的项目需要兼容Swift 5.7以下版本,可以用枚举封装所有合法的JSON类型,从根源避免协议存在类型的限制:
enum JSONValue { case string(String) case int(Int) case double(Double) case bool(Bool) case array([JSONValue]) case dictionary([String: JSONValue]) case null } extension JSONValue { func serialize() throws -> Data { let rawObject = convertToJSONObject() return try JSONSerialization.data(withJSONObject: rawObject) } private func convertToJSONObject() -> Any { switch self { case .string(let str): return str case .int(let num): return num case .double(let num): return num case .bool(let bool): return bool case .array(let arr): return arr.map { $0.convertToJSONObject() } case .dictionary(let dict): return dict.mapValues { $0.convertToJSONObject() } case .null: return NSNull() } } } // 使用示例 let array2: JSONValue = .array([.string("")]) let array1: JSONValue = .array([.string(""), array2]) let dict: JSONValue = .dictionary(["test": .array([.dictionary([:])])]) let data = try? dict.serialize()
Encodable 场景的解决方案
你遇到的Encodable相关报错也是同样的协议存在类型不遵循自身的问题,由于系统的Encodable协议不允许开发者直接声明extension Encodable: Encodable {},推荐你直接使用上述枚举方案,让JSONValue遵循Encodable即可:
extension JSONValue: Encodable { func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch self { case .string(let str): try container.encode(str) case .int(let num): try container.encode(num) case .double(let num): try container.encode(num) case .bool(let bool): try container.encode(bool) case .array(let arr): try container.encode(arr) case .dictionary(let dict): try container.encode(dict) case .null: try container.encodeNil() } } }
内容的提问来源于stack exchange,提问作者Nickkk
相关产品推荐
相关产品推荐

