gpt4 book ai didi

json - 在Swift 3中正确解析JSON

转载 作者:行者123 更新时间:2023-11-30 11:04:06 26 4
gpt4 key购买 nike

我正在尝试获取JSON响应并将结果存储在变量中。在Xcode 8的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)之后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]

最佳答案

首先,永远不要从远程URL同步加载数据,而应始终使用URLSession之类的异步方法。


  “ Any”没有下标成员


发生这种情况是因为编译器不知道中间对象是什么类型(例如currently中的["currently"]!["temperature"]),并且由于您使用的是像NSDictionary这样的Foundation集合类型,所以编译器根本不知道类型。

另外,在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()


要打印 currentConditions的所有键/值对,您可以编写

 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 iable都是可变的,并且传递任何这些选项并将结果分配给 let常量根本无效。此外,大多数实现绝不会改变反序列化的JSON。

在Swift中唯一有用的(稀有)选项是 .allowFragments,如果JSON根对象可以是值类型( StringNumberBoolnull)而不是其中之一,则必须使用 array收集类型( dictionaryoptions)。但通常会省略 []参数,这意味着没有选项。

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

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

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



收集类型为


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


值类型为


字符串-JSON:双引号 "123",甚至 "false"String中的任何值– Swift: 123
数字-JSON:数值不在双引号 123.0Int中–斯威夫特: Doubletrue
布尔-JSON: falsetrue不能用双引号引起来– Swift: falsenull
null-JSON: NSNull – Swift: String


根据JSON规范,字典中的所有键都必须为 {}



基本上,始终建议使用可选绑定安全地解开可选选项

如果根对象是字典( [String:Any]),则将类型强制转换为 OneOfSupportedJSONTypes

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


并使用( []是JSON集合或如上所述的值类型)通过键检索值。

if let foo = parsedData["foo"] as? OneOfSupportedJSONTypes {
print(foo)
}




如果根对象是数组( [[String:Any]]),则将类型强制转换为 .allowFragments

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

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


苹果在Swift博客中发表了一篇详尽的文章: Working with JSON in Swift



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

在Swift 4+中, Weather协议提供了一种更方便的方法来将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 URL。 Swift类型与上述相同。还有一些其他选项:


表示 URL的字符串可以直接解码为 time
可以使用 Date dateDecodingStrategy.secondsSince1970整数解码为 keyDecodingStrategy
可以使用 .convertFromSnakeCase 将snaked_cased JSON密钥转换为camelCase




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


其他可编码来源:


Apple: Encoding and Decoding Custom Types
HackingWithSwift: Codable Cheat Sheet
Ray Wenderlich: Encoding and Decoding in Swift

关于json - 在Swift 3中正确解析JSON,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52940656/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com