use*_*039 118 parsing json swift swift3 xcode8
我正在尝试获取JSON响应并将结果存储在变量中.我已经在Swift的早期版本中使用了此代码的版本,直到GM版本的Xcode 8发布.我看了一下StackOverflow上的一些类似的帖子:Swift 2解析JSON - 无法下标Swift 3中的'AnyObject'和JSON解析类型的值.
但是,似乎那里传达的想法并不适用于这种情况.
如何在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)")
}
}
}
Run Code Online (Sandbox Code Playgroud)
编辑:以下是API调用后的结果示例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]
Run Code Online (Sandbox Code Playgroud)
vad*_*ian 167
首先,永远不要从远程URL同步加载数据,总是使用异步方法URLSession.
'任何'没有下标成员
之所以发生是因为编译器不知道中间对象是什么类型(例如currentlyin ["currently"]!["temperature"]),并且因为您正在使用Foundation集合类型,NSDictionary因此编译器根本不知道该类型.
此外,在Swift 3中,需要通知编译器所有下标对象的类型.
您必须将JSON序列化的结果强制转换为实际类型.
此代码使用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()
Run Code Online (Sandbox Code Playgroud)
要打印currentConditions您可以写的所有键/值对
let currentConditions = parsedData["currently"] as! [String:Any]
for (key, value) in currentConditions {
print("\(key) - \(value) ")
}
Run Code Online (Sandbox Code Playgroud)
关于jsonObject(with data:
许多(似乎都是)教程建议.mutableContainers或.mutableLeaves选项在Swift中完全是无意义的.这两个选项是用于将结果分配给NSMutable...对象的旧Objective-C选项.在Swift中var,默认情况下任何iable都是可变的,并且传递任何这些选项并将结果赋值给let常量根本没有效果.此外,大多数实现都不会改变反序列化的JSON.
唯一的(罕见)选项,在夫特是有用是.allowFragments如果如果JSON根对象可以是一个值类型是必需(String,Number,Bool或null)而不是集合类型中的一个(array或dictionary).但通常省略options参数,这意味着没有选项.
================================================== =========================
JSON是一种排列良好的文本格式.读取JSON字符串非常容易.仔细阅读字符串.只有六种不同的类型 - 两种集合类型和四种值类型.
集合类型是
[]- Swift:[Any]但在大多数情况下[[String:Any]]{}- Swift:[String:Any]值类型是
"Foo",偶数"123"或"false"- Swift:String123或123.0- Swift:Int或Doubletrue或false 不用双引号 - Swift:true或falsenull- Swift:NSNull根据JSON规范,字典中的所有键都必须是String.
基本上,它始终建议使用可选绑定来安全地打开选项
如果根对象是字典({}),则将类型转换为[String:Any]
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [String:Any] { ...
Run Code Online (Sandbox Code Playgroud)
并通过键检索值(OneOfSupportedJSONTypes如上所述是JSON集合或值类型.)
if let foo = parsedData["foo"] as? OneOfSupportedJSONTypes {
print(foo)
}
Run Code Online (Sandbox Code Playgroud)
如果根对象是一个数组([]),则将类型转换为[[String:Any]]
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]] { ...
Run Code Online (Sandbox Code Playgroud)
并使用迭代遍历数组
for item in parsedData {
print(item)
}
Run Code Online (Sandbox Code Playgroud)
如果您需要特定索引处的项目,请检查索引是否存在
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]], parsedData.count > 2,
let item = parsedData[2] as? OneOfSupportedJSONTypes {
print(item)
}
}
Run Code Online (Sandbox Code Playgroud)
在极少数情况下,JSON只是值类型之一 - 而不是集合类型 - 您必须传递.allowFragments选项并将结果转换为适当的值类型,例如
if let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? String { ...
Run Code Online (Sandbox Code Playgroud)
Apple在Swift博客中发表了一篇全面的文章:在Swift中使用JSON
更新:在Swift 4+中,Codable协议提供了一种更方便的方法来将JSON直接解析为结构/类.
dis*_*ilo 12
对于Swift 3,Xcode 8 Beta 6发生的一个重大变化是id现在导入Any而不是AnyObject.
这意味着它将parsedData作为最有可能与该类型的字典返回[Any:Any].如果不使用调试器,我无法确切地告诉你你的演员NSDictionary会做什么,但你看到的错误是因为dict!["currently"]!有类型Any
那么,你是如何解决这个问题的?从你引用它的方式来看,我假设dict!["currently"]!是一本字典,所以你有很多选择:
首先你可以这样做:
let currentConditionsDictionary: [String: AnyObject] = dict!["currently"]! as! [String: AnyObject]
Run Code Online (Sandbox Code Playgroud)
这将为您提供一个字典对象,然后您可以查询值,这样您就可以得到这样的温度:
let currentTemperatureF = currentConditionsDictionary["temperature"] as! Double
Run Code Online (Sandbox Code Playgroud)
或者,如果您愿意,可以在线执行:
let currentTemperatureF = (dict!["currently"]! as! [String: AnyObject])["temperature"]! as! Double
Run Code Online (Sandbox Code Playgroud)
希望这有帮助,我担心我没有时间编写示例应用程序来测试它.
最后一点需要注意的是:最简单的方法可能是在开始时简单地将JSON有效负载转换为[String: AnyObject]正确的.
let parsedData = try JSONSerialization.jsonObject(with: data as Data, options: .allowFragments) as! Dictionary<String, AnyObject>
Run Code Online (Sandbox Code Playgroud)
小智 6
let str = "{\"names\": [\"Bob\", \"Tim\", \"Tina\"]}"
let data = str.data(using: String.Encoding.utf8, allowLossyConversion: false)!
do {
let json = try JSONSerialization.jsonObject(with: data, options: []) as! [String: AnyObject]
if let names = json["names"] as? [String]
{
print(names)
}
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
Run Code Online (Sandbox Code Playgroud)
后来更新了isConnectToNetwork-Function,感谢这篇文章。
我为其编写了一个额外的方法:
import SystemConfiguration
func loadingJSON(_ link:String, postString:String, completionHandler: @escaping (_ JSONObject: AnyObject) -> ()) {
if(isConnectedToNetwork() == false){
completionHandler("-1" as AnyObject)
return
}
let request = NSMutableURLRequest(url: URL(string: link)!)
request.httpMethod = "POST"
request.httpBody = postString.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: request as URLRequest) { data, response, error in
guard error == nil && data != nil else { // check for fundamental networking error
print("error=\(error)")
return
}
if let httpStatus = response as? HTTPURLResponse , httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(response)")
}
//JSON successfull
do {
let parseJSON = try JSONSerialization.jsonObject(with: data!, options: .allowFragments)
DispatchQueue.main.async(execute: {
completionHandler(parseJSON as AnyObject)
});
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
}
task.resume()
}
func isConnectedToNetwork() -> Bool {
var zeroAddress = sockaddr_in(sin_len: 0, sin_family: 0, sin_port: 0, sin_addr: in_addr(s_addr: 0), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)
let defaultRouteReachability = withUnsafePointer(to: &zeroAddress) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {zeroSockAddress in
SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress)
}
}
var flags: SCNetworkReachabilityFlags = SCNetworkReachabilityFlags(rawValue: 0)
if SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) == false {
return false
}
let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
let ret = (isReachable && !needsConnection)
return ret
}
Run Code Online (Sandbox Code Playgroud)
所以现在您可以在应用程序中随时随地轻松调用它
loadingJSON("yourDomain.com/login.php", postString:"email=\(userEmail!)&password=\(password!)") { parseJSON in
if(String(describing: parseJSON) == "-1"){
print("No Internet")
} else {
if let loginSuccessfull = parseJSON["loginSuccessfull"] as? Bool {
//... do stuff
}
}
Run Code Online (Sandbox Code Playgroud)
我正是为此目的构建了quicktype。只需粘贴您的示例 JSON,然后 quicktype 就会为您的 API 数据生成此类型层次结构:
struct Forecast {
let hourly: Hourly
let daily: Daily
let currently: Currently
let flags: Flags
let longitude: Double
let latitude: Double
let offset: Int
let timezone: String
}
struct Hourly {
let icon: String
let data: [Currently]
let summary: String
}
struct Daily {
let icon: String
let data: [Datum]
let summary: String
}
struct Datum {
let precipIntensityMax: Double
let apparentTemperatureMinTime: Int
let apparentTemperatureLowTime: Int
let apparentTemperatureHighTime: Int
let apparentTemperatureHigh: Double
let apparentTemperatureLow: Double
let apparentTemperatureMaxTime: Int
let apparentTemperatureMax: Double
let apparentTemperatureMin: Double
let icon: String
let dewPoint: Double
let cloudCover: Double
let humidity: Double
let ozone: Double
let moonPhase: Double
let precipIntensity: Double
let temperatureHigh: Double
let pressure: Double
let precipProbability: Double
let precipIntensityMaxTime: Int
let precipType: String?
let sunriseTime: Int
let summary: String
let sunsetTime: Int
let temperatureMax: Double
let time: Int
let temperatureLow: Double
let temperatureHighTime: Int
let temperatureLowTime: Int
let temperatureMin: Double
let temperatureMaxTime: Int
let temperatureMinTime: Int
let uvIndexTime: Int
let windGust: Double
let uvIndex: Int
let windBearing: Int
let windGustTime: Int
let windSpeed: Double
}
struct Currently {
let precipProbability: Double
let humidity: Double
let cloudCover: Double
let apparentTemperature: Double
let dewPoint: Double
let ozone: Double
let icon: String
let precipIntensity: Double
let temperature: Double
let pressure: Double
let precipType: String?
let summary: String
let uvIndex: Int
let windGust: Double
let time: Int
let windBearing: Int
let windSpeed: Double
}
struct Flags {
let sources: [String]
let isdStations: [String]
let units: String
}
Run Code Online (Sandbox Code Playgroud)
它还生成无依赖的封送处理代码以将 的返回值哄骗JSONSerialization.jsonObject到 a 中Forecast,包括一个接受 JSON 字符串的便捷构造函数,以便您可以快速解析强类型Forecast值并访问其字段:
let forecast = Forecast.from(json: jsonString)!
print(forecast.daily.data[0].windGustTime)
Run Code Online (Sandbox Code Playgroud)
你可以从 npm 安装 quicktypenpm i -g quicktype或者使用 web UI来获取完整的生成代码以粘贴到你的操场上。