在Swift 3中正确解析JSON

我正在尝试获取JSON响应并将结果存储在变量中。在以前的Swift版本中,我已经使用过此代码的版本,直到Xcode

8的GM版本发布为止。我在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)")

}

}

}

这是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]

回答:

首先, ,而应始终使用异步方法,例如URLSession

‘Any’没有下标成员

是因为编译器没有什么类型的中间对象(例如理念currently["currently"]!["temperature"]),并且由于使用的是基金会类型,如NSDictionary编译器在所有有关的类型不知道。

另外,在Swift 3中,需要通知编译器 下标对象的类型。

此代码的用途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) ")

}

许多(看来都是)教程建议.mutableContainers.mutableLeaves选项在Swift中完全是胡说八道。这两个选项是遗留的Objective-

C选项,用于将结果分配给NSMutable...对象。在Swift中var,默认情况下任何变量都是可变的,并且传递这些选项中的任何一个并将结果分配给let常量都完全无效。此外,大多数实现绝不会改变反序列化的JSON。

唯一的(罕见)选项,在夫特是有用是.allowFragments如果如果JSON根对象可以是一个值类型是必需(StringNumberBoolnull)而不是集合类型中的一个(arraydictionary)。但通常会省略options表示

No options 的参数。

================================================== =========================

解析JSON的一些一般注意事项

JSON是一种排列合理的文本格式。读取JSON字符串非常容易。 。只有六种不同的类型–两种收集类型和四种值类型。


收集类型为

  • 数组 -JSON:方括号中的对象[]-Swift:[Any]但在大多数情况下[[String:Any]]
  • 字典 -JSON:大括号中的对象{}-Swift:[String:Any]

值类型为

  • 字符串 -JSON:双引号中的任何值"Foo",偶数"123""false"– Swift:String
  • 数字 -JSON:数值 双引号123123.0– Swift:IntDouble
  • 布尔 -JSON:truefalse 使用双引号-Swift:truefalse
  • null -JSON:null– Swift:NSNull

根据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只是值类型之一(而不是集合类型),您必须传递.allowFragments选项并将结果转换为适当的值类型,例如

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

苹果在Swift博客中发表了一篇详尽的文章:在Swift中使用JSON


================================================== =========================

在Swift 4+中,该Codable协议提供了一种将JSON直接解析为结构/类的更便捷的方法。

例如,问题中的给定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}

"""

可以解码为struct Weather。Swift类型与上述相同。还有一些其他选项:

  • 表示的字符串URL可以直接解码为URL
  • time整数可以被解码为Date与所述dateDecodingStrategy.secondsSince1970
  • snaked_cased JSON键可被转化为 驼峰keyDecodingStrategy.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)

}

其他可编码来源:

  • 苹果:对自定义类型进行编码和解码
  • HackingWithSwift:可编码备忘单
  • Ray Wenderlich:Swift中的编码和解码

以上是 在Swift 3中正确解析JSON 的全部内容, 来源链接: utcz.com/qa/408958.html

回到顶部