从服务器解析复杂嵌套数据

时间:2017-06-12 03:00:03

标签: ios iphone json swift dictionary

我试图解析从服务器收到的数据,以便将其显示在UIPicker视图中。但是我解析它并将其显示到UIPIcker View中太复杂了。什么是最好的方法,我可以解析以下数据,并为UIPickerView做好准备。 这是试图解析服务器信息的会话

let url = NSURL(string: "http://dummy.com/api")!

    let request = URLRequest(url: url as URL)
URLSession.shared.dataTask(with: request) { data, response, error in

        if error == nil {

            do {
                let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
                guard let parseJSON = json else{

                    print("Error While Parsing")
                    return
                }
                print(parseJSON)

                let responseDic = parseJSON["response"] as? NSDictionary
                let utilityCode = responseDic?["utility_code"] as? String

                if utilityCode == "AFRICELL" {

                    let africellPackages = responseDic?["packages"] as! NSArray

                    print(africellPackages)
                }


            }catch{
                return
            }
        }

        }.resume()

以下数据是发出GET请求时服务器的响应。

{
  "status": "OK",
  "response": [
    {
      "utility_code": "AIRTEL",
      "packages": [
        {
          "package_id": 33,
          "package_name": "Daily 10MB",
          "package_code": "6000",
          "package_price": 300
        },
        {
          "package_id": 34,
          "package_name": "Daily 20MB",
          "package_code": "6002",
          "package_price": 500
        },
        {
          "package_id": 65,
          "package_name": "Weekly Roaming 200MB",
          "package_code": "6030",
          "package_price": 100000
        }
      ]
    },
    {
      "utility_code": "AFRICELL",
      "packages": [
        {
          "package_id": 68,
          "package_name": "Daily 10 MB",
          "package_code": "5000",
          "package_price": 290
        }
      ]
    },
    {
      "utility_code": "SMART",
      "packages": [
        {
          "package_id": 69,
          "package_name": "Daily 50 MB",
          "package_code": "8000",
          "package_price": 500
        }
      ]
    },
    {
      "utility_code": "SMILE",
      "packages": [
        {
          "package_id": 70,
          "package_name": "Smile 1GB",
          "package_code": "7006",
          "package_price": 32000
        }
      ]
    }
  ]
}

干杯并感谢您的帮助!

1 个答案:

答案 0 :(得分:0)

为简洁起见,提供以下所有示例而不进行错误检查。对于生产代码,您应该正确处理错误。

没有任何外部框架的Swift 3

手动解码JSON的大部分工作涉及定义数据模型:

struct JSONResponse {
    var status: String
    var response: [Utility]

    init(jsonDict: [String: AnyObject]) {
        self.status = jsonDict["status"] as! String
        self.response = [Utility]()

        let response = jsonDict["response"] as! [AnyObject]
        for r in response.map({ $0 as! [String: AnyObject] }) {
            let utility = Utility(jsonDict: r)
            self.response.append(utility)
        }
    }
}

struct Utility {
    var code: String
    var packages: [Package]

    init(jsonDict: [String: AnyObject]) {
        self.code = jsonDict["utility_code"] as! String
        self.packages = [Package]()

        let packages = jsonDict["packages"] as! [AnyObject]
        for p in packages.map({ $0 as! [String: AnyObject] }) {
            let package = Package(jsonDict: p)
            self.packages.append(package)
        }
    }
}

struct Package {
    var id: Int
    var name: String
    var code: String
    var price: Int

    init(jsonDict: [String: AnyObject]) {
        self.id = jsonDict["package_id"] as! Int
        self.name = jsonDict["package_name"] as! String
        self.code = jsonDict["package_code"] as! String
        self.price = jsonDict["package_price"] as! Int
    }
}

以及如何使用它:

let jsonDict = try! JSONSerialization.jsonObject(with: data) as! [String: AnyObject]
let jsonResponse = JSONResponse(jsonDict: jsonDict)
let utilities = jsonResponse.response

print(utilities)

使用ObjectMapper的Swift 3

解码JSON是标准Swift的痛苦。您需要一个外部JSON框架,如ObjectMapper,以减轻JSON数据和数据模型之间映射的痛苦。从CocoaPod或Carthage安装ObjectMapper

首先,在单独的文件中定义您的数据模型:

import ObjectMapper

struct JSONResponse : Mappable {
    var status: String?
    var response: [Utility]?

    init?(map: Map) { }
    mutating func mapping(map: Map) {
        self.status     <- map["status"]
        self.response   <- map["response"]
    }
}

struct Utility : Mappable {
    var code: String?
    var packages: [Package]?

    init?(map: Map) { }
    mutating func mapping(map: Map) {
        self.code           <- map["code"]
        self.packages       <- map["packages"]
    }
}

struct Package : Mappable {
    var id: Int?
    var name: String?
    var code: String?
    var price: Int?

    init?(map: Map) { }
    mutating func mapping(map: Map) {
        self.id             <- map["package_id"]
        self.name           <- map["package_name"]
        self.code           <- map["package_code"]
        self.price          <- map["package_price"]
    }
}

然后你可以使用它将JSON映射到你的对象:

// data is what your get in the dataTask's completion block
let jsonString = String(data: data, encoding: .utf8)!
if let jsonResponse = JSONResponse(JSONString: jsonString),
    let utilities = jsonResponse.response {
    print(utilities)
}

所有内容都必须声明为可选项,因为您不知道该值是否在JSON字符串中。

在Swift 4中

使用新的EncodableDecodable协议,Swift 4中的事情变得更加简单。两者结合形成Encodable协议(是的,protocol composition是Swift 4中的新事物)。你不再需要Swift 4中的ObjectMapper

您仍需要首先定义数据模型:

struct JSONResponse : Codable {
    var status: String
    var response: [Utility]
}

struct Utility : Codable {
    var code: String
    var packages: [Package]

    private enum CodingKeys: String, CodingKey {
        case code = "utility_code"
        case packages
    }
}

struct Package : Codable {
    var id: Int
    var name: String
    var code: String
    var price: Int

    private enum CodingKeys: String, CodingKey {
        case id = "package_id"
        case name = "package_name"
        case code = "package_code"
        case price = "package_price"
    }
}

以下是在Swift 4中使用新JSONDecoder结构的方法:

let jsonRepsonse = try! JSONDecoder().decode(JSONResponse.self, from: data)
let utilities = jsonRepsonse.response
print(utilities)

那么这里发生了什么?

ObjectMapperCodable都将JSON中的值映射到您的数据模型。

ObjectMapper中,您必须明确列出哪个属性映射到名为mapping(map:)的函数中JSON中的哪个值:

mutating func mapping(map: Map) {
    self.id             <- map["package_id"]
    self.name           <- map["package_name"]
    self.code           <- map["package_code"]
    self.price          <- map["package_price"]
}

<-是&#34;映射运算符&#34;由ObjectMapper定义(它不在标准库中)。

使用Swift 4&#39; Codable,编译器会为你自动化很多东西,这使得它一开始看起来很神奇而且令人困惑。基本上,您在名为CodingKeys的枚举中定义映射:

struct JSONResponse : Codable {
    var status: String
    var response: [Utility]

    // Define your JSON mappings here       
    private enum CodingKeys: String, CodingKey {
        case status = "status"
        case response = "response"
    }
}

struct Package : Codable {
    var id: Int
    var name: String
    var code: String
    var price: Int

    // Define your JSON mappings here
    private enum CodingKeys: String, CodingKey {
        case id = "package_id"
        case name = "package_name"
        case code = "package_code"
        case price = "package_price"
    }
}

如果您的JSON键与您的属性名称相同,就像在JSONResponse结构中一样,您不必为每个case定义显式值,您只需编写:

struct JSONResponse : Codable {
    var status: String
    var response: [Utility]

    private enum CodingKeys: String, CodingKey {
        case status         // no explicit value here
        case response
    }
}

更好的是,由于每个JSON键与您的属性名称相同,您可以一起省略CodingKeys枚举,让编译器为您处理:

// All you need to do is to conform it to Codable
// CodingKeys is generated automatically
struct JSONResponse : Codable {
    var status: String
    var response: [Utility]
}

要详细了解Codable,请观看2017年WWDC的What's new in Foundation会话。