在Swift 3中正确解析JSON [英] Correctly Parsing JSON in Swift 3

查看:134
本文介绍了在Swift 3中正确解析JSON的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试获取JSON响应并将结果存储在变量中.在以前的Swift版本中,我已经使用过该代码的版本,直到Xcode 8的GM版本发布为止.我在StackOverflow上看到了一些类似的帖子:在Swift 3中进行JSON解析.

I'm trying to fetch a JSON response and store the results in a variable. I've had versions of this code work in previous releases of Swift, until the GM version of Xcode 8 was released. I had a look at a few similar posts on StackOverflow: Swift 2 Parsing JSON - Cannot subscript a value of type 'AnyObject' and JSON Parsing in Swift 3.

但是,似乎传达的想法在这种情况下不适用.

However, it seems the ideas conveyed there do not apply in this scenario.

如何在Swift 3中正确解析JSON响应? 在Swift 3中读取JSON的方式是否有所改变?

How do I correctly parse the JSON reponse in Swift 3? Has something changed in the way JSON is read in Swift 3?

下面是有问题的代码(可以在操场上运行):

Below is the code in question (it can be run in a playground):

import Cocoa

let url = "https://api.forecast.io/forecast/apiKey/37.5673776,122.048951"

if let url = NSURL(string: url) {
    if let data = try? Data(contentsOf: url as URL) {
        do {
            let parsedData = try JSONSerialization.jsonObject(with: data as Data, options: .allowFragments)

        //Store response in NSDictionary for easy access
        let dict = parsedData as? NSDictionary

        let currentConditions = "\(dict!["currently"]!)"

        //This produces an error, Type 'Any' has no subscript members
        let currentTemperatureF = ("\(dict!["currently"]!["temperature"]!!)" as NSString).doubleValue

            //Display all current conditions from API
            print(currentConditions)

            //Output the current temperature in Fahrenheit
            print(currentTemperatureF)

        }
        //else throw an error detailing what went wrong
        catch let error as NSError {
            print("Details of JSON parsing error:\n \(error)")
        }
    }
}

编辑:这是print(currentConditions)

["icon": partly-cloudy-night, "precipProbability": 0, "pressure": 1015.39, "humidity": 0.75, "precipIntensity": 0, "windSpeed": 6.04, "summary": Partly Cloudy, "ozone": 321.13, "temperature": 49.45, "dewPoint": 41.75, "apparentTemperature": 47, "windBearing": 332, "cloudCover": 0.28, "time": 1480846460]

推荐答案

首先永远不要从远程URL同步加载数据,请始终使用URLSession这样的异步方法.

First of all never load data synchronously from a remote URL, use always asynchronous methods like URLSession.

任何"都没有下标成员

'Any' has no subscript members

出现

的原因是,编译器不知道中间对象是什么类型(例如["currently"]!["temperature"]中的currently),并且由于使用的是像NSDictionary这样的Foundation集合类型,所以编译器根本不知道类型

occurs because the compiler has no idea of what type the intermediate objects are (for example currently in ["currently"]!["temperature"]) and since you are using Foundation collection types like NSDictionary the compiler has no idea at all about the type.

此外,在Swift 3中,还需要告知编译器所有下标对象的类型.

Additionally in Swift 3 it's required to inform the compiler about the type of all subscripted objects.

您必须将JSON序列化的结果转换为实际类型.

此代码使用URLSession排他性地 Swift本机类型

This code uses URLSession and exclusively Swift native types

let urlString = "https://api.forecast.io/forecast/apiKey/37.5673776,122.048951"

let url = URL(string: urlString)
URLSession.shared.dataTask(with:url!) { (data, response, error) in
  if error != nil {
    print(error)
  } else {
    do {

      let parsedData = try JSONSerialization.jsonObject(with: data!) as! [String:Any]
      let currentConditions = parsedData["currently"] as! [String:Any]

      print(currentConditions)

      let currentTemperatureF = currentConditions["temperature"] as! Double
      print(currentTemperatureF)
    } catch let error as NSError {
      print(error)
    }
  }

}.resume()

要打印currentConditions的所有键/值对,您可以编写

To print all key / value pairs of currentConditions you could write

 let currentConditions = parsedData["currently"] as! [String:Any]

  for (key, value) in currentConditions {
    print("\(key) - \(value) ")
  }

关于jsonObject(with data的注释:

A note regarding jsonObject(with data:

许多(似乎所有)教程都建议使用.mutableContainers.mutableLeaves选项,这在Swift中完全是胡说八道.这两个选项是遗留的Objective-C选项,用于将结果分配给NSMutable...对象.在Swift中,默认情况下任何var iable都是可变的,并且传递任何这些选项并将结果分配给let常量根本没有任何作用.此外,大多数实现绝不会改变反序列化的JSON.

Many (it seems all) tutorials suggest .mutableContainers or .mutableLeaves options which is completely nonsense in Swift. The two options are legacy Objective-C options to assign the result to NSMutable... objects. In Swift any variable is mutable by default and passing any of those options and assigning the result to a let constant has no effect at all. Further most of the implementations are never mutating the deserialized JSON anyway.

在Swift中有用的唯一(罕见)选项是.allowFragments,如果JSON根对象可以是值类型(StringNumberBoolnull),则必须使用.allowFragments而不是一种收集类型(arraydictionary).但通常会省略options参数,这意味着没有选项.

The only (rare) option which is useful in Swift is .allowFragments which is required if if the JSON root object could be a value type(String, Number, Bool or null) rather than one of the collection types (array or dictionary). But normally omit the options parameter which means No options.

================================================ ==========================

===========================================================================

JSON是一种排列合理的文本格式.读取JSON字符串非常容易. 仔细阅读字符串.只有六种不同的类型-两种收集类型和四种值类型.

JSON is a well-arranged text format. It's very easy to read a JSON string. Read the string carefully. There are only six different types – two collection types and four value types.

集合类型为

  • 数组-JSON:方括号中的对象[]-Swift:[Any],但在大多数情况下[[String:Any]]
  • 字典-JSON:大括号中的对象{}-Swift:[String:Any]
  • Array - JSON: objects in square brackets [] - Swift: [Any] but in most cases [[String:Any]]
  • Dictionary - JSON: objects in curly braces {} - Swift: [String:Any]

值类型为

  • String -JSON:双引号"Foo"中的任何值,甚至"123""false" – Swift:String
  • Number -JSON:双引号123123.0中的数字值 – Swift:IntDouble
  • Bool -JSON:truefalse 双引号– Swift:truefalse
  • -JSON:null – Swift:NSNull
  • String - JSON: any value in double quotes "Foo", even "123"or "false" – Swift: String
  • Number - JSON: numeric values not in double quotes 123 or 123.0 – Swift: Int or Double
  • Bool - JSON: true or false not in double quotes – Swift: true or false
  • null - JSON: null – Swift: NSNull

根据JSON规范,字典中的所有键都必须为String.

According to the JSON specification all keys in dictionaries are required to be String.

基本上,通常建议使用可选绑定安全地解开可选对象

如果根对象是字典({}),则将类型强制转换为[String:Any]

If the root object is a dictionary ({}) cast the type to [String:Any]

if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [String:Any] { ...

并通过键检索值(OneOfSupportedJSONTypes是JSON集合或如上所述的值类型.)

and retrieve values by keys with (OneOfSupportedJSONTypes is either JSON collection or value type as described above.)

if let foo = parsedData["foo"] as? OneOfSupportedJSONTypes {
    print(foo)
} 


如果根对象是数组([]),则将类型强制转换为[[String:Any]]


If the root object is an array ([]) cast the type to [[String:Any]]

if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]] { ...

并使用

for item in parsedData {
    print(item)
}

如果需要在特定索引处的项目,请检查索引是否存在

If you need an item at specific index check also if the index exists

if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]], parsedData.count > 2,
   let item = parsedData[2] as? OneOfSupportedJSONTypes {
      print(item)
    }
}


在极少数情况下,JSON只是值类型之一(而不是集合类型),您必须传递.allowFragments选项并将结果转换为适当的值类型,例如


In the rare case that the JSON is simply one of the value types – rather than a collection type – you have to pass the .allowFragments option and cast the result to the appropriate value type for example

if let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? String { ...

Apple已在Swift博客中发表了一篇详尽的文章:在Swift中使用JSON

Apple has published a comprehensive article in the Swift Blog: Working with JSON in Swift

================================================ ==========================

===========================================================================

例如问题中的给定JSON示例(稍作修改)

For example the given JSON sample in the question (slightly modified)

let jsonString = """
{"icon": "partly-cloudy-night", "precipProbability": 0, "pressure": 1015.39, "humidity": 0.75, "precip_intensity": 0, "wind_speed": 6.04, "summary": "Partly Cloudy", "ozone": 321.13, "temperature": 49.45, "dew_point": 41.75, "apparent_temperature": 47, "wind_bearing": 332, "cloud_cover": 0.28, "time": 1480846460}
"""

可以将

解码为结构Weather. Swift类型与上述相同.还有一些其他选项:

can be decoded into the struct Weather. The Swift types are the same as described above. There are a few additional options:

  • 表示URL的字符串可以直接解码为URL.
  • 可以使用dateDecodingStrategy .secondsSince1970time整数解码为Date.
  • 可以使用keyDecodingStrategy .convertFromSnakeCase
  • snaked_cased JSON密钥转换为 camelCase
  • Strings representing an URL can be decoded directly as URL.
  • The time integer can be decoded as Date with the dateDecodingStrategy .secondsSince1970.
  • snaked_cased JSON keys can be converted to camelCase with the keyDecodingStrategy .convertFromSnakeCase
struct Weather: Decodable {
    let icon, summary: String
    let pressure: Double, humidity, windSpeed : Double
    let ozone, temperature, dewPoint, cloudCover: Double
    let precipProbability, precipIntensity, apparentTemperature, windBearing : Int
    let time: Date
}

let data = Data(jsonString.utf8)
do {
    let decoder = JSONDecoder()
    decoder.dateDecodingStrategy = .secondsSince1970
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    let result = try decoder.decode(Weather.self, from: data)
    print(result)
} catch {
    print(error)
}

其他可编码来源:

  • Apple: Encoding and Decoding Custom Types
  • HackingWithSwift: Codable Cheat Sheet
  • Ray Wenderlich: Encoding and Decoding in Swift

这篇关于在Swift 3中正确解析JSON的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆