You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 04:39:02