【问题标题】:Swift - Parsing JSON and retrieve dataSwift - 解析 JSON 并检索数据
【发布时间】:2018-01-03 12:42:45
【问题描述】:

我在 Xcode 上使用 Swift 并尝试解析 JSON 文件以检索有关附近商店的一些数据。 我的源代码如下:

import GooglePlaces

import SwiftyJSON

class Place {
    let name: String
    let coordinates: CLLocationCoordinate2D

    init(diction:[String : Any])
    {
        let json = JSON(diction)
        name = json["name"].stringValue //as! String

        let lat = json["geometry"]["location"]["lat"].doubleValue as CLLocationDegrees
        let long = json["geometry"]["location"]["lng"].doubleValue as CLLocationDegrees
        coordinates = CLLocationCoordinate2DMake(lat, long)
    }
}

class ViewController: UIViewController, MKMapViewDelegate, SceneLocationViewDelegate {


var urlString = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?"
        urlString += "&location=51.507514,-0.073603"
        urlString += "&radius=1500" //meters
        urlString += "&name=Specsavers"
        urlString += "&key=**************************" 

        guard let url = URL(string: urlString) else {return}
        var places = [Place]()

        var request = URLRequest(url:url)
        request.httpMethod = "GET"

        let task = URLSession.shared.dataTask(with: request as URLRequest) {
            data, response, error in
            print("HEREurlSession")
            if let content = data {
                do {
                    let json = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
                    print(json) // json results are printed fine here
                    if let results = json["results"] as? [[String : Any]] {
                        for place in results {
                            places.append(Place(diction: place))
                        }

                    }
                    else {
                        print("return")
                    }

                }
                catch{

                }
            }
        }
        task.resume()

        let size = places.count
        print("HERE: ", size)

}

构建成功但输出为size = 0,这意味着我没有检索数据并且变量places 为空。 我不知道它是否完全相关,但我收到以下警告:Cast from 'MDLMaterialProperty?!' to unrelated type '[[String : Any]]' always fails for the line if let results = json["results"] as? [[String : Any]] in my source code。

为什么我没有正确解析 JSON 文件,也没有检索到我想要的数据?

【问题讨论】:

  • 不相关,但为什么要将URLRequest 转换为URLRequest?这是没有意义的。顺便说一下,GET 请求根本不需要URLRequest。为什么将mutableContainers 分配给不可变常量?这也没有意义
  • 感谢您的回复。几周后我现在再次捕获此源代码,我不记得从哪里开始,但我确信 SO 中的一个好的教程或一个好的答案建议这样做。从这个意义上说,我认为这不是问题,但如果您坚持这样做,请告诉我。
  • 当然不是问题,这就是我写unrelated的原因。发布 JSON 响应的相关部分会很有帮助。警告显示results 的值不是[[String : Any]]。并且代码末尾的places.count == 0 是正确的,因为dataTask 是异步工作的。
  • 我建议您使用 Alamofire 来处理您的网络请求,它会简化您的生活。还有新的 Codable 协议而不是 SwiftyJSON。
  • 感谢您的回复和建议。我会记住的。

标签: json swift google-places-api


【解决方案1】:

URLSession.shared.dataTask(with:) 是异步的。这意味着,它在后台运行。你正在执行

let size = places.count
print("HERE: ", size)

dataTask 仍在工作时。

相反,您应该在完成处理程序中使用您的结果:

    let task = URLSession.shared.dataTask(with: request as URLRequest) {
        data, response, error in
        print("HEREurlSession")
        if let content = data {
            do {
                let json = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
                                    print(json)
                if let results = json["results"] as? [[String : Any]] {
                    for place in results {
                        places.append(Place(diction: place))
                    }

                }
                else {
                    print("return")
                }

            }
            catch{

            }
        }

        // Use your result here
        let size = places.count
        useResultSize(size)

    }
    task.resume()

    func useResultSize(_ size: Int) {
        // Use your result here
        print("HERE: ", size)
    }

更新

您似乎错过了异步执行的实际含义。让我试着解释一下。

让我们在代码中标记执行顺序:

首先,执行代码的红色部分。程序执行从顶部开始,然后移动到底部的红色框,只有在那之后(一旦网络请求完成),绿色部分才被执行。

也就是说,你只能在代码的绿色部分使用网络请求的结果。在绿色部分之外,不保证结果可用。

如果您遵循我最初的建议,那么一切都会正常进行。请在我的操场上查看成功执行:

【讨论】:

  • 感谢您的回复。我不确定这是否正是问题所在,因为我过去像这样运行它并且效果很好。另外,如果我按照您的建议进行操作,那么如果我在此源代码之后使用 for index in 0...size 之类的 for 循环,则会收到以下错误:Binary operator '...' cannot be applied to operands of type 'Int' and '(UIContentContainer, CGSize) -> CGSize'。我错过了什么?
  • 这是真的很难相信这曾经奏效。正如我所说,URLSession 是异步的,因此需要一些时间才能完成并在后台运行
  • 您的新问题是因为size 在您之前使用它的地方不再可用。我更新了我的代码,向您展示了如何执行此操作的示例。
  • 再次感谢您的回复,但我的输出仍然是size = 0,这是我帖子的主要问题。此外,我在原始帖子中发布的源代码在这个意义上运行良好,因为我得到了输出size=0,尽管您发表了评论。此外,API 密钥工作正常,因为如果我在 Internet 上粘贴并搜索相应的 url,那么我会在浏览器上获得 json 结果。那么您认为问题出在哪里?
  • 请尝试阅读并理解我的cmets。由于我向您解释的原因,它不起作用。你的原始代码永远不会工作,因为代码永远不会使用 API 的结果,也永远不会打印除HERE: 0 之外的任何内容。为什么?正如我解释的那样,因为该方法是异步的。
猜你喜欢
  • 2021-12-26
  • 2020-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-29
  • 2019-04-30
  • 1970-01-01
相关资源
最近更新 更多