在Swift 3中正确解析JSON

11 浏览
0 Comments

在Swift 3中正确解析JSON

我试图获取一个JSON响应并将结果存储在一个变量中。在之前的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的方式有所改变吗?以下是相关代码(可以在playground中运行):\nimport Cocoa\nlet url = \"https://api.forecast.io/forecast/apiKey/37.5673776,122.048951\"\nif let url = NSURL(string: url) {\n if let data = try? Data(contentsOf: url as URL) {\n do {\n let parsedData = try JSONSerialization.jsonObject(with: data as Data, options: .allowFragments)\n //将响应存储在NSDictionary中以便于访问\n let dict = parsedData as? NSDictionary\n let currentConditions = \"\\(dict![\"currently\"]!)\"\n //这会产生一个错误,类型\'Any\'没有下标成员\n let currentTemperatureF = (\"\\(dict![\"currently\"]![\"temperature\"]!!)\" as NSString).doubleValue\n //显示来自API的所有当前状态\n print(currentConditions)\n //以华氏温度输出当前温度\n print(currentTemperatureF)\n }\n //否则抛出一个详细描述出错原因的错误\n catch let error as NSError {\n print(\"JSON解析错误的详细信息:\\n \\(error)\")\n }\n }\n}\n\n编辑:以下是API调用后的结果样例(在print(currentConditions)后):\n[\"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]

0
0 Comments

在Swift 3中正确解析JSON的问题出现的原因是使用`JSONSerialization.jsonObject(with:options:)`方法时,返回的是`Any`类型而不是原始的JSON对象类型。这导致了在访问JSON对象的属性时需要进行强制类型转换。

为了解决这个问题,可以使用可选绑定来检查JSON对象的属性是否存在,并将其转换为正确的类型。在上面的例子中,可以使用可选绑定来检查`json["names"]`是否存在,并将其转换为[String]类型。如果存在,即可打印出names的值。

修复后的代码如下:

let str = "{\"names\": [\"Bob\", \"Tim\", \"Tina\"]}"

let data = str.data(using: String.Encoding.utf8, allowLossyConversion: false)!

do {

if 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)")

}

这样就可以正确解析JSON并访问其属性了。

0
0 Comments

在Swift 3中,Xcode 8 Beta 6的一个重大变化是id现在被导入为Any而不是AnyObject

这意味着parsedData返回的是一个字典,类型很可能是[Any:Any]。没有使用调试器,我无法告诉你将dict强制转换为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>

dict!["currently"]! as! [String: String]会导致崩溃。

它崩溃的点是["temperature"]!!还有试图将String强制转换为NSString-验证了新的解决方法swiftlang.ng.bluemix.net/#/repl/57d3bc683a422409bf36c391

[String: String]根本不会起作用,因为有几个数值。它在Mac Playground中不起作用。

啊,是的,我没有注意到这些,而且Swift沙箱还很贴心地将它们隐藏了!-现在已经更正[再次](并在macOS上进行了测试)。感谢你指出这一点:)

0
0 Comments

在Swift 3中正确解析JSON的问题出现的原因是编译器对中间对象的类型没有了解(例如`["currently"]!["temperature"]`中的`currently`),由于使用了Foundation集合类型如`NSDictionary`,编译器对类型完全没有了解。

解决方法是将JSON序列化的结果转为实际类型。

下面是解决方法的代码示例:

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`的所有key/value对,可以使用以下代码:

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`变量默认都是可变的,而且对于那些从未改变反序列化的JSON的实现,将结果分配给`let`常量根本没有任何效果。在Swift中,唯一(少见的)有用的选项是`allowFragments`,如果JSON的根对象可以是值类型(如`String`,`Number`,`Bool`或`null`)而不是集合类型(`array`或`dictionary`),则需要使用该选项。但通常省略`options`参数,即表示“没有选项”。

一些解析JSON的一般性考虑:

JSON是一种格式良好的文本格式,很容易阅读JSON字符串。JSON只有六种不同的类型,两种集合类型和四种值类型。

集合类型是:

- `Array` - JSON中用方括号`[]`表示,Swift中使用`[Any]`,但在大多数情况下使用`[[String:Any]]`。

- `Dictionary` - JSON中用花括号`{}`表示,Swift中使用`[String:Any]`。

值类型是:

- `String` - JSON中用双引号括起来的任何值,如`"Foo"`,甚至`"123"`或`"false"`,在Swift中使用`String`类型。

- `Number` - JSON中的数字值,不用双引号括起来,如`123`或`123.0`,在Swift中使用`Int`或`Double`类型。

- `Bool` - JSON中的`true`或`false`,不用双引号括起来,在Swift中使用`true`或`false`。

- `null` - JSON中的`null`,在Swift中使用`NSNull`。

根据JSON规范,字典中的所有键都必须是`String`类型。

对于根对象是字典(`{}`)的情况,可以将类型转为`[String:Any]`,并通过键来获取值。示例代码如下:

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

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)

}

}

在很少的情况下,JSON只是一个值类型,而不是集合类型。这种情况下,需要传递`.allowFragments`选项,并将结果转为适当的值类型。示例代码如下:

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

// Handle the string value

}

在Swift 4+中,可以使用`Codable`协议更方便地将JSON直接解析为结构体/类。示例代码如下:

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 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}

"""

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)

}

在Swift 3中,可能需要使用`JSONSerialization.ReadingOptions.mutableContainers`选项,而不是空数组作为选项参数。另外,为了安全地解包可选值,建议始终使用可选绑定。

异步连接非常重要,因为同步调用会阻塞调用者(应用程序)直到接收到响应,导致应用程序看起来像是挂起了。

以上就是解析JSON的问题的原因和解决方法。希望对你有帮助!

0