我正在尝试获取 JSON 响应并将结果存储在变量中。在 Xcode 8 的 GM 版本发布之前,我已经在以前的 Swift 版本中使用了此代码的版本。我在 StackOverflow 上看了一些类似的帖子:Swift 2 Parsing JSON - Cannot subscript a value of type 'AnyObject'和 JSON Parsing in Swift 3 .
但是,那里传达的想法似乎不适用于这种情况。
如何正确解析 Swift 3 中的 JSON 响应?
在 Swift 3 中读取 JSON 的方式有什么改变吗?
下面是有问题的代码(它可以在操场上运行):
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)
之后 API 调用的结果示例["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
.
'Any' has no subscript members
currently
中的 ["currently"]!["temperature"]
)并且因为您使用的是 Foundation 集合类型,例如 NSDictionary
编译器根本不知道类型。URLSession
和 独家 Swift 原生类型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
的所有键/值对你可以写 let currentConditions = parsedData["currently"] as! [String:Any]
for (key, value) in currentConditions {
print("\(key) - \(value) ")
}
jsonObject(with data
的说明:.mutableContainers
或 .mutableLeaves
选项在 Swift 中完全是无稽之谈。这两个选项是传统的 Objective-C 选项,用于将结果分配给 NSMutable...
对象。在 Swift 中,任何 var
默认情况下,iable 是可变的,并传递任何这些选项并将结果分配给 let
常数根本没有影响。此外,大多数实现无论如何都不会改变反序列化的 JSON。.allowFragments
如果 JSON 根对象可以是值类型( String
、 Number
、 Bool
或 null
)而不是集合类型之一( array
或 dictionary
),则这是必需的。但通常省略options
参数,表示无选项。[]
- swift :[Any]
但在大多数情况下 [[String:Any]]
{}
- swift :[String:Any]
"Foo"
,甚至 "123"
或 "false"
– swift :String
123
或 123.0
– swift :Int
或 Double
true
或 false
不是 双引号 - Swift:true
或 false
null
– swift :NSNull
String
.{}
),则将类型强制转换为 [String:Any]
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [String:Any] { ...
OneOfSupportedJSONTypes
是 JSON 集合或值类型,如上所述。)if let foo = parsedData["foo"] as? OneOfSupportedJSONTypes {
print(foo)
}
[]
),则将类型强制转换为 [[String:Any]]
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]] { ...
for item in parsedData {
print(item)
}
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]], parsedData.count > 2,
let item = parsedData[2] as? OneOfSupportedJSONTypes {
print(item)
}
}
.allowFragments
选项并将结果转换为适当的值类型,例如if let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? String { ...
Codable
协议(protocol)提供了一种更方便的方法来将 JSON 直接解析为结构/类。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 类型与上述相同。还有一些额外的选项:URL
的字符串可以直接解码为URL
. time
整数可以解码为 Date
与 dateDecodingStrategy
.secondsSince1970
. keyDecodingStrategy
将snaked_cased JSON key 转换为camelCase .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)
}
https://stackoverflow.com/questions/39423367/