我正在尝试获取一个JSON响应并将结果存储在一个变量中。在Xcode8的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)
之后调用的结果示例
["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]
发布于 2016-09-10 15:32:41
首先,永远不要从远程URL同步加载数据,总是使用像URLSession
这样的异步方法。
'Any‘没有下标成员
发生这种情况是因为编译器不知道中间对象是什么类型(例如["currently"]!["temperature"]
中的currently
),而且由于您使用的是像NSDictionary
这样的基础集合类型,所以编译器对这种类型一无所知。
此外,在Swift 3中,需要通知编译器所有下标对象的类型。
您必须将序列化的结果强制转换为实际类型。
此代码仅使用 Swift本机类型的URLSession
和
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中完全是胡说八道。这两个选项是用于将结果分配给NSMutable...
对象的遗留Objective-C选项。在Swift中,任何var
iable在默认情况下都是可变的,传递这些选项中的任何一个并将结果赋给let
常量都没有任何效果。而且,大多数实现都不会改变反序列化的JSON。
在Swift中唯一有用的(稀有)选项是.allowFragments
,如果JSON根对象可以是值类型(String
、Number
、Bool
或null
)而不是集合类型(array
或dictionary
),则需要该选项。但通常会忽略options
参数,这意味着没有选项。
===========================================================================
解析JSON的一些一般注意事项
JSON是一种排列良好的文本格式。读取JSON字符串非常容易。仔细阅读字符串。只有六种不同的类型- two集合类型和四种值类型。
集合类型为
Swift数组- [String:Any]
:方括号中的对象-
[Any]
但在大多数情况下,[[String:Any]]
[]
- Swift:[Any]
值类型为
Swift字符串- JSON:位于双引号"Foo"
中的任何值,甚至是"123"
或"false"
-
String
123
或123.0
- Swift:Int
或false
true
或false
不是用双引号括起来的E268>- Swift: D69123>或123.0
- JSON:null
- Swift:根据JSON规范,字典中的所有键都必须是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)
}
}
在极少数情况下,JSON仅仅是- rather而不是集合类型的值类型之一-您必须传递.allowFragments
选项并将结果转换为适当的值类型,例如
if let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? String { ...
苹果公司在Swift博客上发表了一篇综合文章:Working with JSON in Swift
===========================================================================
在Swift中,Codable
协议提供了一种将4+直接解析为结构/类的更方便的方法。
例如,问题中给定的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
。可以使用.secondsSince1970
..convertFromSnakeCase
将
time
整数解码为Date
可以使用dateDecodingStrategy
JSON将JSON密钥转换为camelCasestruct 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)
}
其他可编码源:
发布于 2016-09-10 15:34:27
在Swift 3的Xcode8Beta6中发生的一个很大的变化是,id现在作为Any
而不是AnyObject
导入。
这意味着parsedData
作为最有可能具有[Any:Any]
类型的字典返回。如果不使用调试器,我无法确切地告诉您转换为NSDictionary
将做什么,但是您看到的错误是因为dict!["currently"]!
的类型为Any
那么,你怎么解决这个问题呢?从你引用它的方式来看,我假设dict!["currently"]!
是一个字典,所以你有很多选择:
首先,你可以这样做:
let currentConditionsDictionary: [String: AnyObject] = dict!["currently"]! as! [String: AnyObject]
这将给你一个字典对象,然后你可以查询它的值,所以你可以像这样得到你的温度:
let currentTemperatureF = currentConditionsDictionary["temperature"] as! Double
或者,如果你愿意,你可以排成一排:
let currentTemperatureF = (dict!["currently"]! as! [String: AnyObject])["temperature"]! as! Double
希望这能有所帮助,恐怕我还没有时间写一个示例应用程序来测试它。
最后一点:最简单的做法可能是在一开始就将JSON有效负载转换为[String: AnyObject]
。
let parsedData = try JSONSerialization.jsonObject(with: data as Data, options: .allowFragments) as! Dictionary<String, AnyObject>
发布于 2017-01-24 13:19:51
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)")
}
https://stackoverflow.com/questions/39423367
复制相似问题