我对 iOS 编程还比较陌生。然而,我认为 Swift 会有一种自动方式将对象转换为 JSON,反之亦然。话虽这么说,我找到了几个可以做到这一点的库。
然而...
似乎无论您如何将数据发布到 Web 服务(甚至使用 AlamoFire 之类的东西),请求都必须是字典。所有这些论坛都展示了将返回的 JSON 字符串转换为对象是多么容易的示例。真的。但请求需要手动编码。也就是说,遍历所有对象属性并将它们映射为字典。
所以我的问题是:我错过了什么吗?我是否都搞错了,有一种超级简单的方法可以 (a) 在 REQUEST 中发送 JSON(而不是字典)或 (b) 自动将对象转换为字典?
我再次看到处理 JSON 响应是多么容易。我只是在寻找一种自动方法,将我想要发布到 Web 服务的请求对象转换为 AlamoFire(或其他)等库所需的格式。对于其他语言来说,这是相当微不足道的,所以我希望 Swift 有一种同样简单且自动化的方法。
我一定不同意@Darko。
在 Swift 2,
使用面向协议的编程和Mirror类提供的简单反射:
protocol JSONAble {}
extension JSONAble {
func toDict() -> [String:Any] {
var dict = [String:Any]()
let otherSelf = Mirror(reflecting: self)
for child in otherSelf.children {
if let key = child.label {
dict[key] = child.value
}
}
return dict
}
}
然后您可以将此协议与您的请求类一起使用并生成所需的字典:
class JsonRequest : JSONAble {
var param1 : String?
// ...
}
let request = JsonRequest()
// set params of the request
let dict = request.toDict()
// use your dict
我的解决方案是这样的:
extension Encodable {
var dict : [String: Any]? {
guard let data = try? JSONEncoder().encode(self) else { return nil }
guard let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String:Any] else { return nil }
return json
}
}
使用方式如下:
movies.compactMap { $0.dict }
Swift 目前不支持像 Java 或 C# 这样的高级反射,所以答案是:不,没有一种与纯 Swift 一样简单且自动化的方法。
[更新] Swift 4 同时具有
Codable
协议,允许序列化到 JSON 和 PLIST。
typealias Codable = Decodable & Encodable
不使用反射,适用于嵌套对象(Swift 4):
protocol Serializable {
var properties:Array<String> { get }
func valueForKey(key: String) -> Any?
func toDictionary() -> [String:Any]
}
extension Serializable {
func toDictionary() -> [String:Any] {
var dict:[String:Any] = [:]
for prop in self.properties {
if let val = self.valueForKey(key: prop) as? String {
dict[prop] = val
} else if let val = self.valueForKey(key: prop) as? Int {
dict[prop] = val
} else if let val = self.valueForKey(key: prop) as? Double {
dict[prop] = val
} else if let val = self.valueForKey(key: prop) as? Array<String> {
dict[prop] = val
} else if let val = self.valueForKey(key: prop) as? Serializable {
dict[prop] = val.toDictionary()
} else if let val = self.valueForKey(key: prop) as? Array<Serializable> {
var arr = Array<[String:Any]>()
for item in (val as Array<Serializable>) {
arr.append(item.toDictionary())
}
dict[prop] = arr
}
}
return dict
}
}
只需为要转换的自定义对象实现属性和 valueForKey 即可。例如:
class Question {
let title:String
let answer:Int
init(title:String, answer:Int) {
self.title = title
self.answer = answer
}
}
extension Question : Serializable {
var properties: Array<String> {
return ["title", "answer"]
}
func valueForKey(key: String) -> Any? {
switch key {
case "title":
return title
case "answer":
return answer
default:
return nil
}
}
}
如果需要,您可以在 toDictionary 函数中添加更多值类型。
我在 Stack Overflow 上进行大量挖掘后发现的最新解决方案是:
//This block of code used to convert object models to json string
let jsonData = try JSONEncoder().encode(requestData)
let jsonString = String(data: jsonData, encoding: .utf8)!
print(jsonString)
//This method is used to convert jsonstring to dictionary [String:Any]
func jsonToDictionary(from text: String) -> [String: Any]? {
guard let data = text.data(using: .utf8) else { return nil }
let anyResult = try? JSONSerialization.jsonObject(with: data, options: [])
return anyResult as? [String: Any]
}
//Use above method something like this
let params = jsonToDictionary(from: jsonString) ?? [String : Any]()
//Use params to pass in paramters
Alamofire.request(completeUrl, method: .post, parameters: params, encoding:JSONEncoding.prettyPrinted, headers: myHeaders){
response in
//Do whatever you want with response of it.
}
注:
您还可以使用 ObjectMapper 库。它有一个“toJSON”方法,可以将您的对象转换为字典。
简而言之
let dict = Mirror(reflecting: self).children.map({ $0 }).reduce(into: [:]) { $0[$1.label] = $1.value }
如何使用 Mirror 并转换为特定字典类型的示例:
protocol DictionaryConvertible { }
extension DictionaryConvertible {
func toDictionary() -> [String: CustomStringConvertible] {
Dictionary(
uniqueKeysWithValues: Mirror(reflecting: self).children
.compactMap { child in
if let label = child.label,
let value = child.value as? CustomStringConvertible {
return (label, value)
} else {
return nil
}
}
)
}
}
型号:
struct User: Codable {
let name: String?
let number: String?
}
添加此扩展程序:
extension Encodable {
func asDictionary() throws -> [String: Any] {
let data = try JSONEncoder().encode(self)
guard let dictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] else {
throw NSError()
}
return dictionary
}
}
用法(魔法):
try! userObj.asDictionary()