在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

我试图获取一个JSON响应并将结果存储在一个变量中。在之前的Swift版本中,我已经有了这个代码的版本,直到Xcode 8的GM版本发布。我看了一些关于StackOverflow的类似帖子:和

然而,这里传达的想法似乎不适用于这种情况

如何正确解析Swift 3中的JSON响应? Swift 3中JSON的读取方式是否发生了变化

下面是有问题的代码(它可以在操场上运行):

编辑:以下是
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”
    “false”
    –Swift:
    String
  • 数字-JSON:数字值双引号
    123
    123.0
    –Swift:
    Int
    double
  • Bool-JSON:
    true
    false
    双引号–Swift:
    true
    false
  • 空-JSON:
    null
    –Swift:
    NSNull
根据JSON规范,字典中的所有键都必须是
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

其他可编码来源:


Swift 3的Xcode 8 Beta 6的一个重大变化是id现在作为
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)
    }