在Swift 3中正确解析JSON
我试图获取一个JSON响应并将结果存储在一个变量中。在之前的Swift版本中,我已经有了这个代码的版本,直到Xcode 8的GM版本发布。我看了一些关于StackOverflow的类似帖子:和 然而,这里传达的想法似乎不适用于这种情况 如何正确解析Swift 3中的JSON响应? Swift 3中JSON的读取方式是否发生了变化 下面是有问题的代码(它可以在操场上运行): 编辑:以下是在Swift 3中正确解析JSON,json,swift,parsing,swift3,xcode8,Json,Swift,Parsing,Swift3,Xcode8,我试图获取一个JSON响应并将结果存储在一个变量中。在之前的Swift版本中,我已经有了这个代码的版本,直到Xcode 8的GM版本发布。我看了一些关于StackOverflow的类似帖子:和 然而,这里传达的想法似乎不适用于这种情况 如何正确解析Swift 3中的JSON响应? Swift 3中JSON的读取方式是否发生了变化 下面是有问题的代码(它可以在操场上运行): 编辑:以下是print(currentConditions) 首先,切勿从远程URL同步加载数据,请始终使用异步方法,如UR
print(currentConditions)
首先,切勿从远程URL同步加载数据,请始终使用异步方法,如
URLSession
“Any”没有下标成员
之所以发生,是因为编译器不知道中间对象是什么类型(例如<代码>当前<代码> > <代码> > [ [温度] ] /代码>,并且由于使用的是基础集合类型,如<代码> NS字典> /COD> >编译器根本不知道类型。
此外,在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(带数据)的说明:
许多(似乎全部)教程建议使用.mutableContainers
或.mutableLeaves
选项,这在Swift中是完全没有意义的。这两个选项是传统的Objective-C选项,用于将结果分配给NSMutable…
对象。在Swift中,任何var
iable默认情况下都是可变的,并传递这些选项中的任何一个并分配结果对于let
常量没有任何效果。此外,大多数实现无论如何都不会改变反序列化的JSON
在Swift中唯一有用的(罕见)选项是.allowFragments
,如果JSON根对象可以是值类型(String
、Number
、Bool
或null
),而不是集合类型之一(数组
或字典
),则需要该选项。但通常省略选项
参数,这意味着没有选项
===========================================================================
解析JSON的一些一般注意事项
JSON是一种排列良好的文本格式。读取JSON字符串非常容易。仔细阅读字符串。只有六种不同的类型–两种集合类型和四种值类型
集合类型为
- 数组-JSON:方括号中的对象
-Swift:[]
但在大多数情况下[Any]
[[String:Any]]
- Dictionary-JSON:大括号中的对象
-Swift:{}
[String:Any]
- String-JSON:双引号中的任意值
,甚至“Foo”
或“123”
–Swift:“false”
String
- 数字-JSON:数字值不双引号
或123
–Swift:123.0
或Int
double
- Bool-JSON:
或true
非false
true
或false
null
–Swift:NSNull
String
通常建议使用可选绑定来安全地打开可选绑定 如果根对象是字典(
{}
),则将类型强制转换为[String:Any]
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [String:Any] { ...
并使用(其中一个SupportedJSONTypes
是JSON集合或如上所述的值类型)键检索值
如果根对象是数组(
[]
),则将类型强制转换为[[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 4+中,
Codable
协议提供了将JSON直接解析为结构/类的更方便的方法。
例如,问题中给定的JSON示例(稍微修改)
可以解码到结构Weather
。Swift类型与上述相同。有几个附加选项:
- 表示
的字符串可以直接解码为URL
URL
整数可以使用时间
日期解码策略
解码为.secondsSince1970
日期
- snaked_cased JSON密钥可以使用
keyDecodingStrategy
.convertFromSnakeCase
其他可编码来源:
Any
导入,而不是AnyObject
这意味着parsedData
作为最有可能的字典返回,类型为[Any: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)
}
}
if let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? String { ...
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: 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)
}
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
let parsedData = try JSONSerialization.jsonObject(with: data as Data, options: .allowFragments) as! Dictionary<String, AnyObject>
import SystemConfiguration
func loadingJSON(_ link:String, postString:String, completionHandler: @escaping (_ JSONObject: AnyObject) -> ()) {
if(isConnectedToNetwork() == false){
completionHandler("-1" as AnyObject)
return
}
let request = NSMutableURLRequest(url: URL(string: link)!)
request.httpMethod = "POST"
request.httpBody = postString.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: request as URLRequest) { data, response, error in
guard error == nil && data != nil else { // check for fundamental networking error
print("error=\(error)")
return
}
if let httpStatus = response as? HTTPURLResponse , httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(response)")
}
//JSON successfull
do {
let parseJSON = try JSONSerialization.jsonObject(with: data!, options: .allowFragments)
DispatchQueue.main.async(execute: {
completionHandler(parseJSON as AnyObject)
});
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
}
task.resume()
}
func isConnectedToNetwork() -> Bool {
var zeroAddress = sockaddr_in(sin_len: 0, sin_family: 0, sin_port: 0, sin_addr: in_addr(s_addr: 0), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)
let defaultRouteReachability = withUnsafePointer(to: &zeroAddress) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {zeroSockAddress in
SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress)
}
}
var flags: SCNetworkReachabilityFlags = SCNetworkReachabilityFlags(rawValue: 0)
if SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) == false {
return false
}
let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
let ret = (isReachable && !needsConnection)
return ret
}
loadingJSON("yourDomain.com/login.php", postString:"email=\(userEmail!)&password=\(password!)") { parseJSON in
if(String(describing: parseJSON) == "-1"){
print("No Internet")
} else {
if let loginSuccessfull = parseJSON["loginSuccessfull"] as? Bool {
//... do stuff
}
}
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)")
}
let forecast = Forecast.from(json: jsonString)!
print(forecast.daily.data[0].windGustTime)
let urlString = "https://api.forecast.io/forecast/apiKey/37.5673776,122.048951"
let url = URL.init(string: urlString)
URLSession.shared.dataTask(with:url!) { (data, response, error) in
guard error == nil else {
print(error)
}
do {
let Data = try JSONSerialization.jsonObject(with: data!) as! [String:Any]
// Note if your data is coming in Array you should be using [Any]()
//Now your data is parsed in Data variable and you can use it normally
let currentConditions = Data["currently"] as! [String:Any]
print(currentConditions)
let currentTemperatureF = currentConditions["temperature"] as! Double
print(currentTemperatureF)
} catch let error as NSError {
print(error)
}
}.resume()
let json: Dictionary<String, Any>? = ["current": ["temperature": 10]]
/// Curry
public func curry<A, B, C>(_ f: @escaping (A, B) -> C) -> (A) -> (B) -> C {
return { a in
{ f(a, $0) }
}
}
/// Function that takes key and optional dictionary and returns optional value
public func extract<Key, Value>(_ key: Key, _ json: Dictionary<Key, Any>?) -> Value? {
return json.flatMap {
cast($0[key])
}
}
/// Function that takes key and return function that takes optional dictionary and returns optional value
public func extract<Key, Value>(_ key: Key) -> (Dictionary<Key, Any>?) -> Value? {
return curry(extract)(key)
}
/// Precedence group for our operator
precedencegroup RightApplyPrecedence {
associativity: right
higherThan: AssignmentPrecedence
lowerThan: TernaryPrecedence
}
/// Apply. g § f § a === g(f(a))
infix operator § : RightApplyPrecedence
public func §<A, B>(_ f: (A) -> B, _ a: A) -> B {
return f(a)
}
/// Wrapper around operator "as".
public func cast<A, B>(_ a: A) -> B? {
return a as? B
}
let temperature = (extract("temperature") § extract("current") § json) ?? NSNotFound
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)")
}
{
"User":[
{
"FirstUser":{
"name":"John"
},
"Information":"XY",
"SecondUser":{
"name":"Tom"
}
}
]
}
import Foundation
struct RootClass : Codable {
let user : [Users]?
enum CodingKeys: String, CodingKey {
case user = "User"
}
init(from decoder: Decoder) throws {
let values = try? decoder.container(keyedBy: CodingKeys.self)
user = try? values?.decodeIfPresent([Users].self, forKey: .user)
}
}
struct Users : Codable {
let firstUser : FirstUser?
let information : String?
let secondUser : SecondUser?
enum CodingKeys: String, CodingKey {
case firstUser = "FirstUser"
case information = "Information"
case secondUser = "SecondUser"
}
init(from decoder: Decoder) throws {
let values = try? decoder.container(keyedBy: CodingKeys.self)
firstUser = try? FirstUser(from: decoder)
information = try? values?.decodeIfPresent(String.self, forKey: .information)
secondUser = try? SecondUser(from: decoder)
}
}
struct SecondUser : Codable {
let name : String?
enum CodingKeys: String, CodingKey {
case name = "name"
}
init(from decoder: Decoder) throws {
let values = try? decoder.container(keyedBy: CodingKeys.self)
name = try? values?.decodeIfPresent(String.self, forKey: .name)
}
}
struct FirstUser : Codable {
let name : String?
enum CodingKeys: String, CodingKey {
case name = "name"
}
init(from decoder: Decoder) throws {
let values = try? decoder.container(keyedBy: CodingKeys.self)
name = try? values?.decodeIfPresent(String.self, forKey: .name)
}
}
do {
let res = try JSONDecoder().decode(RootClass.self, from: data)
print(res?.user?.first?.firstUser?.name ?? "Yours optional value")
} catch {
print(error)
}