【问题标题】:Deserialize a JSON array to a Swift array of objects将 JSON 数组反序列化为 Swift 对象数组
【发布时间】:2016-09-06 02:01:47
【问题描述】:

我是 Swift 新手,无法弄清楚如何将 JSON 数组反序列化为 Swift 对象数组。我可以将单个 JSON 用户反序列化为 Swift 用户对象,但不确定如何处理 JSON 用户数组。

这是我的 User.swift 类:

class User {
    var id: Int
    var firstName: String?
    var lastName: String?
    var email: String
    var password: String?

    init (){
        id = 0
        email = ""
    }

    init(user: NSDictionary) {
        id = (user["id"] as? Int)!
        email = (user["email"] as? String)!

        if let firstName = user["first_name"] {
            self.firstName = firstName as? String
        }

        if let lastName = user["last_name"] {
            self.lastName = lastName as? String
        }

        if let password = user["password"] {
            self.password = password as? String
        }
     }
}

这是我试图反序列化 JSON 的类:

//single user works.
Alamofire.request(.GET, muURL/user)
         .responseJSON { response in
                if let user = response.result.value {
                    var swiftUser = User(user: user as! NSDictionary)
                }
          }

//array of users -- not sure how to do it. Do I need to loop?
Alamofire.request(.GET, muURL/users)
         .responseJSON { response in
                if let users = response.result.value {
                    var swiftUsers = //how to get [swiftUsers]?
                }
          }

【问题讨论】:

  • 你为什么不使用AlamofireObjectMapper库?
  • 哦,我不知道我可以使用它。我认为这只是为了 REST。我要试一试。
  • 另外,电子邮件是用户的必填字段。将其保留为 Swift 中的必填字段是最佳做法,还是将其设为 Optional 会更好——以防 JSON 由于某种原因没有该值?

标签: ios json swift swift2


【解决方案1】:

最好的方法是使用Alamofire提供的Generic Response Object Serialization这里是一个例子:

1) 在您的 API Manager 或单独的文件中添加扩展程序

    public protocol ResponseObjectSerializable {
        init?(response: NSHTTPURLResponse, representation: AnyObject)
    }

    extension Request {
        public func responseObject<T: ResponseObjectSerializable>(completionHandler: Response<T, NSError> -> Void) -> Self {
            let responseSerializer = ResponseSerializer<T, NSError> { request, response, data, error in
                guard error == nil else { return .Failure(error!) }

                let JSONResponseSerializer = Request.JSONResponseSerializer(options: .AllowFragments)
                let result = JSONResponseSerializer.serializeResponse(request, response, data, error)

                switch result {
                case .Success(let value):
                    if let
                        response = response,
                        responseObject = T(response: response, representation: value)
                    {
                        return .Success(responseObject)
                    } else {
                        let failureReason = "JSON could not be serialized into response object: \(value)"
                        let error = Error.errorWithCode(.JSONSerializationFailed, failureReason: failureReason)
                        return .Failure(error)
                    }
                case .Failure(let error):
                    return .Failure(error)
                }
            }

            return response(responseSerializer: responseSerializer, completionHandler: completionHandler)
        }
    }

public protocol ResponseCollectionSerializable {
    static func collection(response response: NSHTTPURLResponse, representation: AnyObject) -> [Self]
}

extension Alamofire.Request {
    public func responseCollection<T: ResponseCollectionSerializable>(completionHandler: Response<[T], NSError> -> Void) -> Self {
        let responseSerializer = ResponseSerializer<[T], NSError> { request, response, data, error in
            guard error == nil else { return .Failure(error!) }

            let JSONSerializer = Request.JSONResponseSerializer(options: .AllowFragments)
            let result = JSONSerializer.serializeResponse(request, response, data, error)

            switch result {
            case .Success(let value):
                if let response = response {
                    return .Success(T.collection(response: response, representation: value))
                } else {
                    let failureReason = "Response collection could not be serialized due to nil response"
                    let error = Error.errorWithCode(.JSONSerializationFailed, failureReason: failureReason)
                    return .Failure(error)
                }
            case .Failure(let error):
                return .Failure(error)
            }
        }

        return response(responseSerializer: responseSerializer, completionHandler: completionHandler)
    }
}

2) 像这样更新你的模型对象:

final class User: ResponseObjectSerializable, ResponseCollectionSerializable {
    let username: String
    let name: String

    init?(response: NSHTTPURLResponse, representation: AnyObject) {
        self.username = response.URL!.lastPathComponent!
        self.name = representation.valueForKeyPath("name") as! String
    }

    static func collection(response response: NSHTTPURLResponse, representation: AnyObject) -> [User] {
        var users: [User] = []

        if let representation = representation as? [[String: AnyObject]] {
            for userRepresentation in representation {
                if let user = User(response: response, representation: userRepresentation) {
                    users.append(user)
                }
            }
        }

        return users
    }
}

3) 那么你可以这样使用它:

Alamofire.request(.GET, "http://example.com/users")
         .responseCollection { (response: Response<[User], NSError>) in
             debugPrint(response)
         }

来源:Generic Response Object Serialization

有用链接:Alamofire JSON Serialization of Objects and Collections

【讨论】:

  • 谢谢,试试看!
  • 我在上一个项目中使用了它,我发现它是一个优雅的解决方案,因为映射已经是 Almofire 中的一个功能,最好不要延迟到其他第三方库以避免任何兼容性问题未来
  • 如何处理可选字段?说它是:让名字:字符串?
  • 从 JSON 绑定不应该是模型的一部分。使用模型类的协议扩展,如“protocol ResponseObjectSerializable”不是一个好主意,因为在这种情况下,您不能使用解析用户对象的不同变体。有用于 Swift JSON 绑定和验证的新库 - Bender github.com/ptiz/bender 排除了此类错误。
  • @Prabhu 你可以使用self.name = representation.valueForKeyPath("name") as? String
【解决方案2】:

我会遍历它们,然后将每个用户添加到一个数组中(最好是 VC 的属性,而不是实例变量),但这里是一个示例。

    Alamofire.request(.GET, "YourURL/users")
        .responseJSON { response in

            if let users = response.result.value {

                for user in users {

                    var swiftUser = User(user: user as! NSDictionary)

                    //should ideally be a property of the VC
                    var userArray : [User]

                    userArray.append(swiftUser)
                }
            }
    }

【讨论】:

  • 我不太确定 JSON 从响应中看起来如何,因此您可能需要对其进行一些调整,但这至少应该给您一个想法。
  • 使用这个方法我得到一个错误,说 Type AnyObject 不符合协议 SequenceType。知道那是什么吗?
  • 尝试将 for 循环更改为“for user in users as![AnyObject]”,而不仅仅是“for user in users”
  • 使用as! [AnyObject]user as! NSDictionary 并不安全。 if let users = response.result.value as? [String: AnyObject] { 更快捷。
【解决方案3】:

既然您使用 Alamofire 提出请求,为什么不给 Hearst-DD ObjectMapper 一个机会,它有一个 Alamofire 扩展程序 AlamofireObjectMapper。我认为这会节省您的时间!

【讨论】:

  • 看起来很有趣。
【解决方案4】:

你也可以试试 EVReflection https://github.com/evermeer/EVReflection 它更简单,即解析 JSON(代码 sn-p 取自 EVReflection 链接):

let json:String = "{
    \"id\": 24, 
    \"name\": \"Bob Jefferson\",
    \"friends\": [{
        \"id\": 29, 
        \"name\": 
        \"Jen Jackson\"}]}"

你可以使用这个类:

class User: EVObject {
    var id: Int = 0
    var name: String = ""
    var friends: [User]? = []
}

这样:

let user = User(json: json)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-14
    • 1970-01-01
    相关资源
    最近更新 更多