如何将 Swift 对象转换为字典

问题描述 投票:0回答:9

我对 iOS 编程还比较陌生。然而,我认为 Swift 会有一种自动方式将对象转换为 JSON,反之亦然。话虽这么说,我找到了几个可以做到这一点的库。

然而...

似乎无论您如何将数据发布到 Web 服务(甚至使用 AlamoFire 之类的东西),请求都必须是字典。所有这些论坛都展示了将返回的 JSON 字符串转换为对象是多么容易的示例。真的。但请求需要手动编码。也就是说,遍历所有对象属性并将它们映射为字典。

所以我的问题是:我错过了什么吗?我是否都搞错了,有一种超级简单的方法可以 (a) 在 REQUEST 中发送 JSON(而不是字典)或 (b) 自动将对象转换为字典?

我再次看到处理 JSON 响应是多么容易。我只是在寻找一种自动方法,将我想要发布到 Web 服务的请求对象转换为 AlamoFire(或其他)等库所需的格式。对于其他语言来说,这是相当微不足道的,所以我希望 Swift 有一种同样简单且自动化的方法。

json swift dictionary service alamofire
9个回答
50
投票

我一定不同意@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

20
投票

我的解决方案是这样的:

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 }

10
投票

Swift 目前不支持像 Java 或 C# 这样的高级反射,所以答案是:不,没有一种与纯 Swift 一样简单且自动化的方法。

[更新] Swift 4 同时具有

Codable
协议,允许序列化到 JSON 和 PLIST。

typealias Codable = Decodable & Encodable

6
投票

不使用反射,适用于嵌套对象(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 函数中添加更多值类型。


5
投票

我在 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.
        
    }

注:

  1. 我结合了多个答案的解决方案。
  2. 我将这个解决方案与 alamofire 一起使用,因为 alamofire 只接受这种格式的参数“[String:Any]”。

3
投票

您还可以使用 ObjectMapper 库。它有一个“toJSON”方法,可以将您的对象转换为字典。


0
投票

简而言之

let dict = Mirror(reflecting: self).children.map({ $0 }).reduce(into: [:]) { $0[$1.label] = $1.value }

0
投票

如何使用 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
                    }
                }
        )
    }
}

0
投票

型号:

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()
© www.soinside.com 2019 - 2024. All rights reserved.