草庐IT

json - 在 Swift 3 中正确解析 JSON

coder 2023-04-29 原文

我正在尝试获取 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' has no subscript members



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

此外,在 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 )而不是集合类型之一( arraydictionary ),则这是必需的。但通常省略options参数,表示无选项。

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

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

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

集合类型是
  • 数组 - JSON:方括号中的对象 [] - swift :[Any]但在大多数情况下 [[String:Any]]
  • 字典 - JSON:花括号中的对象 {} - swift :[String:Any]

  • 值类型是
  • 字符串 - JSON:双引号中的任何值 "Foo" ,甚至 "123""false" – swift :String
  • 数字 - JSON:数值 不是 双引号 123123.0 – swift :IntDouble
  • bool 值 - JSON:truefalse 不是 双引号 - Swift:truefalse
  • null - JSON:null – swift :NSNull

  • 根据 JSON 规范,字典中的所有键都必须是 String .

    基本上总是建议使用可选绑定(bind)来安全地解包可选值

    如果根对象是字典( {} ),则将类型强制转换为 [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 { ...
    

    Apple 在 Swift 博客中发表了一篇综合文章:Working with JSON in Swift

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

    在 Swift 4+ 中 Codable协议(protocol)提供了一种更方便的方法来将 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整数可以解码为 DatedateDecodingStrategy .secondsSince1970 .
  • 可以使用 keyDecodingStrategy 将snaked_cased JSON key 转换为camelCase .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)
    }
    

    其他可编码来源:
  • 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/39423367/

    有关json - 在 Swift 3 中正确解析 JSON的更多相关文章

    1. Ruby 解析字符串 - 2

      我有一个字符串input="maybe(thisis|thatwas)some((nice|ugly)(day|night)|(strange(weather|time)))"Ruby中解析该字符串的最佳方法是什么?我的意思是脚本应该能够像这样构建句子:maybethisissomeuglynightmaybethatwassomenicenightmaybethiswassomestrangetime等等,你明白了......我应该一个字符一个字符地读取字符串并构建一个带有堆栈的状态机来存储括号值以供以后计算,还是有更好的方法?也许为此目的准备了一个开箱即用的库?

    2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

      我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

    3. ruby - 用逗号、双引号和编码解析 csv - 2

      我正在使用ruby​​1.9解析以下带有MacRoman字符的csv文件#encoding:ISO-8859-1#csv_parse.csvName,main-dialogue"Marceu","Giveittohimóhe,hiswife."我做了以下解析。require'csv'input_string=File.read("../csv_parse.rb").force_encoding("ISO-8859-1").encode("UTF-8")#=>"Name,main-dialogue\r\n\"Marceu\",\"Giveittohim\x97he,hiswife.\"\

    4. ruby-on-rails - Rails HTML 请求渲染 JSON - 2

      在我的Controller中,我通过以下方式在我的index方法中支持HTML和JSON:respond_todo|format|format.htmlformat.json{renderjson:@user}end在浏览器中拉起它时,它会自然地以HTML呈现。但是,当我对/user资源进行内容类型为application/json的curl调用时(因为它是索引方法),我仍然将HTML作为响应。如何获取JSON作为响应?我还需要说明什么? 最佳答案 您应该将.json附加到请求的url,提供的格式在routes.rb的路径中定义。这

    5. ruby-on-rails - 我更新了 ruby​​ gems,现在到处都收到解析树错误和弃用警告! - 2

      简而言之错误:NOTE:Gem::SourceIndex#add_specisdeprecated,useSpecification.add_spec.Itwillberemovedonorafter2011-11-01.Gem::SourceIndex#add_speccalledfrom/opt/local/lib/ruby/site_ruby/1.8/rubygems/source_index.rb:91./opt/local/lib/ruby/gems/1.8/gems/rails-2.3.8/lib/rails/gem_dependency.rb:275:in`==':und

    6. ruby-on-rails - 如何使用 Rack 接收 JSON 对象 - 2

      我有一个非常简单的RubyRack服务器,例如:app=Proc.newdo|env|req=Rack::Request.new(env).paramspreq.inspect[200,{'Content-Type'=>'text/plain'},['Somebody']]endRack::Handler::Thin.run(app,:Port=>4001,:threaded=>true)每当我使用JSON对象向服务器发送POSTHTTP请求时:{"session":{"accountId":String,"callId":String,"from":Object,"headers":

    7. ruby - 用 YAML.load 解析 json 安全吗? - 2

      我正在使用ruby2.1.0我有一个json文件。例如:test.json{"item":[{"apple":1},{"banana":2}]}用YAML.load加载这个文件安全吗?YAML.load(File.read('test.json'))我正在尝试加载一个json或yaml格式的文件。 最佳答案 YAML可以加载JSONYAML.load('{"something":"test","other":4}')=>{"something"=>"test","other"=>4}JSON将无法加载YAML。JSON.load("

    8. ruby - 如何使用 Nokogiri 解析纯 HTML 表格? - 2

      我想用Nokogiri解析HTML页面。页面的一部分有一个表,它没有使用任何特定的ID。是否可以提取如下内容:Today,3,455,34Today,1,1300,3664Today,10,100000,3444,Yesterday,3454,5656,3Yesterday,3545,1000,10Yesterday,3411,36223,15来自这个HTML:TodayYesterdayQntySizeLengthLengthSizeQnty345534345456563113003664354510001010100000344434113622315

    9. python - 帮我找到合适的 ruby​​/python 解析器生成器 - 2

      我使用的第一个解析器生成器是Parse::RecDescent,它的指南/教程很棒,但它最有用的功能是它的调试工具,特别是tracing功能(通过将$RD_TRACE设置为1来激活)。我正在寻找可以帮助您调试其规则的解析器生成器。问题是,它必须用python或ruby​​编写,并且具有详细模式/跟踪模式或非常有用的调试技术。有人知道这样的解析器生成器吗?编辑:当我说调试时,我并不是指调试python或ruby​​。我指的是调试解析器生成器,查看它在每一步都在做什么,查看它正在读取的每个字符,它试图匹配的规则。希望你明白这一点。赏金编辑:要赢得赏金,请展示一个解析器生成器框架,并说明它的

    10. ruby-on-rails - Rails 渲染带有驼峰命名法的 json 对象 - 2

      我在一个简单的RailsAPI中有以下Controller代码:classApi::V1::AccountsControllerehead:not_foundendendend问题在于,生成的json具有以下格式:{id:2,name:'Simpleaccount',cash_flows:[{id:1,amount:34.3,description:'simpledescription'},{id:2,amount:1.12,description:'otherdescription'}]}我需要我生成的json是camelCase('cashFlows'而不是'cash_flows'

    随机推荐