【问题标题】:Swift 4 JSONSerialization.jsonObjectSwift 4 JSONSerialization.jsonObject
【发布时间】:2018-01-06 19:21:29
【问题描述】:

我正在使用 Xcode 9.2 和 Swift 4。如何检查返回的 json 数据是否为空? 现在它在if json[0]["data"] 行给了我错误Type 'Any' has no subscript members

var json: NSMutableArray = []
var newsArray: NSMutableArray = []

let url = URLFactory()
var data = try! NSData(contentsOf: url.getURL()) as Data
do {
    json = try JSONSerialization.jsonObject(with: data, options: []) as! NSMutableArray
    if json[0]["data"] {
        // data is not null
    }
} catch let error as NSError {
    // handle error
}

我的 JSON 返回如下内容:

{
   "data":
   [
      {
         "news_id":123,
         "title":"title",
         "news_date":"2017-02-08 21:46:06",
         "news_url":"url",
         "short_description":"description",
         "category_id":4,
         "category_name":"Health",
         "latlng":
         [
            {
               "lat":"43.003429",
               "lng":"-78.696335"
            }
         ]
      }
      { ....
}

【问题讨论】:

  • 不要在 Swift 中使用 NS[Mutable]ArrayNS[Mutable]Dictionary。使用原生集合类型。
  • 请阅读 JSON。这很容易。只有两种集合类型:{} 是字典,[] 是数组。根对象显然是一个字典 ([String:Any])。并且 has no subscript members 发生错误,因为在 Swift 3+ 中,编译器必须知道任何下标集合类型的静态类型。
  • 不要使用 任何 类型的 NS 类型,包括 NSDataNSError 等。

标签: json swift swift4


【解决方案1】:

如果你使用的是 Swift 4,我可能会建议 JSONDecoder:

首先,定义保存解析数据的类型:

struct ResponseObject: Codable {
    let data: [NewsItem]
}

struct NewsItem: Codable {
    let newsId: Int
    let title: String
    let newsDate: Date
    let newsURL: URL
    let shortDescription: String
    let categoryID: Int
    let categoryName: String
    let coordinates: [Coordinate]

    // because your json keys don't follow normal Swift naming convention, use CodingKeys to map these property names to JSON keys

    enum CodingKeys: String, CodingKey {
        case newsId = "news_id"
        case title
        case newsDate = "news_date"
        case newsURL = "news_url"
        case shortDescription = "short_description"
        case categoryID = "category_id"
        case categoryName = "category_name"
        case coordinates = "latlng"
    }
}

struct Coordinate: Codable {
    let lat: String
    let lng: String
}

然后就可以解析了:

let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(formatter)
do {
    let responseObject = try decoder.decode(ResponseObject.self, from: data)
    print(responseObject.data)
} catch {
    print(error)
}

显然,如果您的 JSON 不同,您可能需要相应地更改您的对象(例如,latlng 是一个坐标数组让我感到奇怪)。此外,如果您想将其中一些字符串转换为数字,您可以定义自定义 init(from:) 方法,但我宁愿修复 JSON(为什么 latlng 返回字符串值而不是数值)。

有关详细信息,请参阅Encoding and Decoding Custom Types


顺便说一句,我建议不要使用这种模式(注意,这是您的网络请求逻辑,但已删除 NSData):

let data = try! Data(contentsOf: url.getURL())

这将同步检索数据,这可能是有问题的,因为

  • 在检索数据时应用程序将被冻结,导致用户体验不佳;
  • 您的应用程序可能会被寻找冻结应用程序的看门狗进程杀死;和
  • 您没有强大的错误处理能力,如果网络请求失败,这将崩溃。

我建议使用URLSession:

let task = URLSession.shared.dataTask(with: url.getURL()) { data, _, error in
    guard let data = data, error == nil else {
        print(error ?? "Unknown error")
        return
    }

    // now parse `data` like shown above

    // if you then need to update UI or model objects, dispatch that back
    // to the main queue:

    DispatchQueue.main.async {
        // use `responseObject.data` to update model objects and/or UI here
    }
}
task.resume()

【讨论】:

  • 数据库中的潜在值是十进制类型(10,6)我不知道这是否是使它返回为字符串的原因! news_id 是数据库中的整数类型,以整数形式返回。
猜你喜欢
  • 2017-03-20
  • 1970-01-01
  • 2019-06-11
  • 2019-03-14
  • 2021-06-26
  • 1970-01-01
  • 2020-07-26
  • 1970-01-01
  • 2017-02-28
相关资源
最近更新 更多