我已经在Swift的早期版本中使用了此代码的版本,直到Xcode 8的GM版本发布 . 我看了一下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)")
}
}
}
Edit: 以下是 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]
6 回答
首先 never load data synchronously from a remote URL ,始终使用异步方法,如
URLSession
.之所以发生是因为编译器不知道中间对象是什么类型的(例如
currently
在["currently"]!["temperature"]
中),并且由于您使用的是基础集合类型,如NSDictionary
,编译器根本不知道该类型 .此外,在Swift 3中,需要通知编译器 all 下标对象的类型 .
You have to cast the result of the JSON serialization to the actual type.
此代码使用
URLSession
和 exclusively Swift本机类型要打印
currentConditions
的所有键/值对,您可以编写A note regarding jsonObject(with data:
许多(似乎都是)教程建议
.mutableContainers
或.mutableLeaves
选项在Swift中完全是无意义的 . 这两个选项是遗留的Objective-C选项,用于将结果分配给NSMutable...
对象 . 在Swift中,默认情况下var
iable是可变的,并且传递任何这些选项并将结果赋值给let
常量根本没有效果 . 此外,大多数实现都不会改变反序列化的JSON .在Swift中唯一有用的(罕见)选项是
.allowFragments
,如果JSON根对象可以是值类型(String
,Number
,Bool
或null
)而不是其中一种集合类型(array
或dictionary
),则需要该选项 . 但通常省略options
参数,这意味着没有选项 .================================================== =========================
解析JSON的一些常规注意事项
JSON是一种排列良好的文本格式 . 读取JSON字符串非常容易 . Read the string carefully . 只有六种不同的类型 - 两种集合类型和四种值类型 .
集合类型是
数组 - JSON:方括号中的对象
[]
- Swift:[Any]
但在大多数情况下[[String:Any]]
Dictionary - JSON:花括号中的对象
{}
- Swift:[String:Any]
值类型是
String - JSON:双引号中的任何值
"Foo"
,偶数"123"
或"false"
- Swift:String
Number - JSON:数字值 not 用双引号
123
或123.0
- Swift:Int
或Double
Bool - JSON:
true
或false
not 双引号 - Swift:true
或false
null - JSON:
null
- Swift:NSNull
根据JSON规范,字典中的所有键都必须是
String
.Basically it's always recommeded to use optional bindings to unwrap optionals safely
如果根对象是字典(
{}
),则将类型强制转换为[String:Any]
并按键检索值(
OneOfSupportedJSONTypes
是JSON集合或值类型,如上所述 . )如果根对象是一个数组(
[]
),则将类型转换为[[String:Any]]
并使用迭代遍历数组
如果您需要特定索引处的项目,请检查索引是否存在
在极少数情况下,JSON只是值类型之一 - 而不是集合类型 - 您必须传递
.allowFragments
选项并将结果转换为适当的值类型,例如Apple在Swift博客上发表了一篇全面的文章:Working with JSON in Swift
Update :在Swift 4中,
Codable
协议提供了一种更方便的方法,可以将JSON直接解析为结构/类 .Xcode 8 Beta 6为Swift 3发生的一个重大变化是id现在导入为
Any
而不是AnyObject
.这意味着
parsedData
作为字典返回,类型为[Any:Any]
. 在没有使用调试器的情况下,我无法准确地告诉您对NSDictionary
的转换是什么,但是您看到的错误是因为dict!["currently"]!
的类型为Any
那么,你是如何解决这个问题的?从你引用它的方式来看,我认为
dict!["currently"]!
是一本字典,所以你有很多选择:第一你可以这样做:
这将为您提供一个字典对象,然后您可以查询值,这样您就可以得到这样的温度:
或者,如果您愿意,可以在线执行:
希望这有帮助,我担心我没有时间编写示例应用程序来测试它 .
最后要注意的一件事:最简单的事情可能是在开始时将JSON有效负载简单地转换为
[String: AnyObject]
.我为此目的 Build 了quicktype . 只需粘贴您的示例JSON,quicktype就会为您的API数据生成此类型层次结构:
它还生成无依赖性编组代码,以便将
JSONSerialization.jsonObject
的返回值强制转换为Forecast
,包括带有JSON字符串的便捷构造函数,以便您可以快速解析强类型Forecast
值并访问其字段:您可以使用
npm i -g quicktype
或use the web UI从npm安装quicktype,以将完整生成的代码粘贴到您的游乐场 .Updated 之后是isConnectToNetwork-Function,感谢这篇文章Check for internet connection with Swift
我为它写了一个额外的方法:
所以现在您可以随时随地在您的应用中调用此功能
问题在于API交互方法 . 仅在语法中更改JSON解析 . 主要问题在于获取数据的方式 . 您使用的是获取数据的同步方式 . 这并不适用于所有情况 . 您应该使用的是获取数据的异步方法 . 通过这种方式,您必须通过API请求数据并等待它响应数据 . 您可以使用URL会话和第三方库(如Alamofire)实现此目的 . 以下是URL会话代码方法 .