当前位置: 首页 > 知识库问答 >
问题:

在Swift3中正确解析JSON

龚沛
2023-03-14

我试图获取一个JSON响应,并将结果存储在一个变量中。我在以前的Swift版本中使用过这个代码版本,直到Xcode8的GM版本发布。我在StackOverflow上看了几篇类似的文章:Swift 2 Parsing JSON-Cant下标类型为“anyObject”的值和Swift 3中的JSON Parsing。

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

如何正确解析Swift3中的JSON响应?Swift3中读取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]

共有1个答案

靳睿
2023-03-14

首先,永远不要从远程URL同步加载数据,始终使用异步方法,如urlsession

“any”没有下标成员

发生的原因是编译器不知道中间对象是什么类型(例如[“currentary”]![“temperature”]中的currentary),并且由于您使用的是诸如nsdictionary之类的基础集合类型,编译器根本不知道该类型。

此外,在Swift3中,需要通知编译器所有下标对象的类型。

您必须将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中,默认情况下任何variable都是可变的,传递这些选项中的任何一个并将结果赋给let常量根本不起作用。此外,大多数实现都从未改变反序列化的JSON。

在Swift中唯一有用的(罕见的)选项是.allowfragments,如果JSON根对象可以是值类型(stringnumberboolnull),而不是集合类型(arraydictionary)时,则需要该选项。但通常省略options参数,这意味着没有选项。

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

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

集合类型为

  • 数组-json:方括号中的对象[]-swift:[Any]但大多数情况下[[string:Any]]
  • 字典-json:大括号中的对象{}-swift:[string:any]

值类型为

  • 字符串-json:双引号中的任何值“foo”,甚至“123”“false”-swift:字符串
  • number-json:不带双引号的数值123123.0-swift:intdouble
  • bool-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

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

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

可以解码为结构weather。Swift类型与上述相同。还有一些附加选项:

    表示 URL
  • 字符串可以直接解码为URL
  • time整数可以通过dateDecodingStrategy.secondssince1970解码为date
  • snaked_cased JSON密钥可以通过keydecodingstrategy.convertfromsnakecase
  • 转换为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:对自定义类型进行编码和解码
  • HackingWithSwift:Codable小抄
  • Ray Wenderlich:Swift中的编码和解码
 类似资料:
  • 我试图获取一个JSON响应,并将结果存储在一个变量中。我在以前的Swift版本中使用过这个代码版本,直到Xcode8的GM版本发布。我在StackOverflow上看了几篇类似的文章:Swift 2 Parsing JSON-Cant下标类型为“anyObject”的值和Swift 3中的JSON Parsing。 然而,这里所传达的思想似乎不适用于这种情况。 如何正确解析Swift3中的JSON

  • 问题内容: 我是Python的新手。我想解析一个csv文件,以便它可以识别带引号的值-例如 1997年,福特E350,“超级豪华卡车” 应该拆分为 (“ 1997”,“福特”,“ E350”,“超级豪华卡车”) 并不是 (“ 1997”,“福特”,“ E350”,“超级”,“豪华卡车””) 以上就是我使用类似的东西所得到的。 我该怎么做呢?同样最好将这些值存储在数组或其他数据结构中吗?因为在我从c

  • 代码: 这很奇怪,因为BeansException继承了Throwable:http://static.springsource.org/spring/docs/2.0.2/api/org/springframework/beans/BeansException.html。原因是什么,如何修复?

  • 问题内容: 我有一个非常棘手的问题,我希望有人能对此有所启发。我正在使用Jquery从另一个网站(我拥有)检索Http响应。收到DOM后,我将对其进行解析以获取某些信息。但是,当我尝试获取链接的href属性时,IE会将本地域添加到href的开头! 这是我的代码: 我的变量PageLink应该是“ /pages/getThisPage.aspx?id=8347”。但是,它将作为“ http://my

  • 我有一个自由标记 ftl 模板文件,该文件包含在 IntelliJ 项目中的标签库中。问题是 JspTaglibs 在 IntelliJ 中没有解决。该错误突出显示文本 ,错误消息为“无法解析变量'JspTaglibs'”。文件内容如下: 这是一个导入的Maven项目。包含的ftl位于一个已编译的JAR文件中,我将它作为内容根包含在一个Web模块中。 关于解析“JspTaglibs”以便可以为自由

  • 在Rails中,我查询数据库以构建highcharts的数据对象。 这是我的控制器中的方法: 终止 现在,在我看来,我正试图在一个脚本中把它拉出来。 ---脚本--- ---脚本--- 当我打印到控制台时,我可以看到对象及其所有数据。此外,当我打印到html时,输出看起来是正确的: "[{\"名称\":\"原 但是当我去打印数据变量时,它是null(显然是由于不是有效输入)。我搞砸了什么?