Swift - 使用协议定义递归类型
Swift - define a recursive type with a protocol
我想定义一个可以序列化为有效 JSON 对象的类型
例如,如果 JSON 可以包含以下内容:
String
Number
Array
Object
Date
Boolean
我想定义一个具有有效类型的协议
protocol JsonSerializable {
}
typealias JSONObject = [String : JsonSerializable]
typealias JSONArray = [JsonSerializable]
// foundation implements serializing numbers + strings + dates etc. to JSON
extension String : JsonSerializable {}
extension Int : JsonSerializable {}
// problem with defining dictionary and array of JSON-able types
extension Dictionary : JsonSerializable {}
...
问题是如何确保字典仅包含可序列化类型(在编译时)
据我所知,今天 Xcode 7 beta 6 是不可能的。
如果需要,您可以复制此雷达:http://openradar.appspot.com/radar?id=5623386654900224
首先,我建议您阅读 Empowering Extensions in Swift 2: Protocols, Types and Subclasses (Xcode 7 beta 2)。 (因为它是针对 Beta 2 的,所以有一些小的变化)
回到你的问题。对于 Array
:
extension Array where Element: JsonSerializable {
var json: String { ... }
}
[1, 2, 3].json // Valid
[true, false].json // Invalid; `Bool` doesn't conform to `JsonSerializable`.
字典有点棘手,因为正如提到的文章中所说:
The current rules of extending a generic type in this way is that the
type being referenced after the where keyword must be a class or a
protocol.
因此您不能指定 Dictionary
的 Key
必须是 String
。文章中给出的解决方法是定义一个StringType
协议:
protocol StringType {
var characters: String.CharacterView { get }
}
extension String: StringType {}
现在用于词典扩展:
extension Dictionary where Key: StringType, Value: JsonSerializable {
var json: String { ... }
}
["A": 1, "B": 2].json // Valid
[1: "1", 2: "2"].json // Invalid; `Int` doesn't conform to `StringType`.
["A": true, "B": false].json // Invalid; `Bool` doesn't conform to `JsonSerializable`.
或者,您可以创建自己的 JsonArray
和 JsonDictionary
类型,它们分别由 Array
或 Dictionary
支持:
struct JsonArray<Element: JsonSerializable> {
private var array: [Element]
...
}
extension JsonArray: ArrayLiteralConvertible {
init(arrayLiteral elements: Element...) {
self.init(array: elements)
}
}
struct JsonDictionary<Value: JsonSerializable> {
private var dictionary: [String: Value]
...
}
extension JsonDictionary: DictionaryLiteralConvertible {
init(dictionaryLiteral elements: (String, Value)...) {
var temp = [String: Value]()
for (key, value) in elements {
temp[key] = value
}
self.init(dictionary: temp)
}
}
let array: JsonArray = [1, 2, 3]
let dictionary: JsonDictionary = ["A": 1, "B": 2]
我想定义一个可以序列化为有效 JSON 对象的类型
例如,如果 JSON 可以包含以下内容:
String
Number
Array
Object
Date
Boolean
我想定义一个具有有效类型的协议
protocol JsonSerializable {
}
typealias JSONObject = [String : JsonSerializable]
typealias JSONArray = [JsonSerializable]
// foundation implements serializing numbers + strings + dates etc. to JSON
extension String : JsonSerializable {}
extension Int : JsonSerializable {}
// problem with defining dictionary and array of JSON-able types
extension Dictionary : JsonSerializable {}
...
问题是如何确保字典仅包含可序列化类型(在编译时)
据我所知,今天 Xcode 7 beta 6 是不可能的。
如果需要,您可以复制此雷达:http://openradar.appspot.com/radar?id=5623386654900224
首先,我建议您阅读 Empowering Extensions in Swift 2: Protocols, Types and Subclasses (Xcode 7 beta 2)。 (因为它是针对 Beta 2 的,所以有一些小的变化)
回到你的问题。对于 Array
:
extension Array where Element: JsonSerializable {
var json: String { ... }
}
[1, 2, 3].json // Valid
[true, false].json // Invalid; `Bool` doesn't conform to `JsonSerializable`.
字典有点棘手,因为正如提到的文章中所说:
The current rules of extending a generic type in this way is that the type being referenced after the where keyword must be a class or a protocol.
因此您不能指定 Dictionary
的 Key
必须是 String
。文章中给出的解决方法是定义一个StringType
协议:
protocol StringType {
var characters: String.CharacterView { get }
}
extension String: StringType {}
现在用于词典扩展:
extension Dictionary where Key: StringType, Value: JsonSerializable {
var json: String { ... }
}
["A": 1, "B": 2].json // Valid
[1: "1", 2: "2"].json // Invalid; `Int` doesn't conform to `StringType`.
["A": true, "B": false].json // Invalid; `Bool` doesn't conform to `JsonSerializable`.
或者,您可以创建自己的 JsonArray
和 JsonDictionary
类型,它们分别由 Array
或 Dictionary
支持:
struct JsonArray<Element: JsonSerializable> {
private var array: [Element]
...
}
extension JsonArray: ArrayLiteralConvertible {
init(arrayLiteral elements: Element...) {
self.init(array: elements)
}
}
struct JsonDictionary<Value: JsonSerializable> {
private var dictionary: [String: Value]
...
}
extension JsonDictionary: DictionaryLiteralConvertible {
init(dictionaryLiteral elements: (String, Value)...) {
var temp = [String: Value]()
for (key, value) in elements {
temp[key] = value
}
self.init(dictionary: temp)
}
}
let array: JsonArray = [1, 2, 3]
let dictionary: JsonDictionary = ["A": 1, "B": 2]