更新时间:2022-11-19 08:04:45
首先永远不要从远程 URL 同步加载数据,始终使用像 URLSession
这样的异步方法.
First of all never load data synchronously from a remote URL, use always asynchronous methods like URLSession
.
'Any' 没有下标成员
'Any' has no subscript members
发生是因为编译器不知道中间对象是什么类型(例如 currently
在 ["currently"]!["temperature"]
中)并且因为您正在使用像 NSDictionary
这样的 Foundation 集合类型,编译器根本不知道该类型.
occurs because the compiler has no idea of what type the intermediate objects are (for example currently
in ["currently"]!["temperature"]
) and since you are using Foundation collection types like NSDictionary
the compiler has no idea at all about the type.
此外,在 Swift 3 中,需要通知编译器关于所有下标对象的类型.
Additionally in Swift 3 it's required to inform the compiler about the type of all subscripted objects.
您必须将 JSON 序列化的结果转换为实际类型.
此代码使用URLSession
和独占 Swift 原生类型
This code uses URLSession
and exclusively Swift native types
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
的所有键/值对,您可以编写
To print all key / value pairs of currentConditions
you could write
let currentConditions = parsedData["currently"] as! [String:Any]
for (key, value) in currentConditions {
print("(key) - (value) ")
}
关于jsonObject(with data
)的说明:
A note regarding jsonObject(with data
:
许多(似乎都是)教程建议 .mutableContainers
或 .mutableLeaves
选项,这在 Swift 中完全是无稽之谈.这两个选项是遗留的 Objective-C 选项,用于将结果分配给 NSMutable...
对象.在 Swift 中,默认情况下任何 var
iable 都是可变的,传递任何这些选项并将结果分配给 let
常量根本没有任何影响.此外,大多数实现无论如何都不会改变反序列化的 JSON.
Many (it seems all) tutorials suggest .mutableContainers
or .mutableLeaves
options which is completely nonsense in Swift. The two options are legacy Objective-C options to assign the result to NSMutable...
objects. In Swift any var
iable is mutable by default and passing any of those options and assigning the result to a let
constant has no effect at all. Further most of the implementations are never mutating the deserialized JSON anyway.
唯一(罕见)在 Swift 中有用的选项是 .allowFragments
如果 JSON 根对象可以是值类型(String
, >Number
、Bool
或 null
)而不是其中一种集合类型(array
或 dictionary
).但通常省略 options
参数,这意味着 No options.
The only (rare) option which is useful in Swift is .allowFragments
which is required if if the JSON root object could be a value type(String
, Number
, Bool
or null
) rather than one of the collection types (array
or dictionary
). But normally omit the options
parameter which means No options.
============================================================================
===========================================================================
JSON 是一种排列整齐的文本格式.读取 JSON 字符串非常容易.仔细阅读字符串.只有六种不同的类型——两种集合类型和四种值类型.
JSON is a well-arranged text format. It's very easy to read a JSON string. Read the string carefully. There are only six different types – two collection types and four value types.
集合类型是
[]
- Swift:[Any]
但在大多数情况下 [[String]:Any]]
{}
- Swift:[String:Any]
[]
- Swift: [Any]
but in most cases [[String:Any]]
{}
- Swift: [String:Any]
值类型是
"Foo"
中的任何值,甚至 "123"
或 "false"代码> – Swift:String
123
或 123.0
- Swift:Int
或 Double
true
或 false
not - Swift:true
或 false
null
- Swift:NSNull
"Foo"
, even "123"
or "false"
– Swift: String
123
or 123.0
– Swift: Int
or Double
true
or false
not in double quotes – Swift: true
or false
null
– Swift: NSNull
根据 JSON 规范,字典中的所有键都必须是 String
.
According to the JSON specification all keys in dictionaries are required to be String
.
基本上总是推荐使用可选绑定来安全地解包可选
如果根对象是字典 ({}
) 将类型转换为 [String:Any]
If the root object is a dictionary ({}
) cast the type to [String:Any]
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [String:Any] { ...
并通过键检索值 (OneOfSupportedJSONTypes
是 JSON 集合或值类型,如上所述.)
and retrieve values by keys with (OneOfSupportedJSONTypes
is either JSON collection or value type as described above.)
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 you need an item at specific index check also if the index exists
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]], parsedData.count > 2,
let item = parsedData[2] as? OneOfSupportedJSONTypes {
print(item)
}
}
在 JSON 只是一种值类型而不是集合类型的极少数情况下,您必须传递 .allowFragments
选项并将结果转换为适当的值类型,例如
In the rare case that the JSON is simply one of the value types – rather than a collection type – you have to pass the .allowFragments
option and cast the result to the appropriate value type for example
if let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? String { ...
Apple 在 Swift 博客中发表了一篇综合文章:在 Swift 中使用 JSON
Apple has published a comprehensive article in the Swift Blog: Working with JSON in Swift
============================================================================
===========================================================================
例如问题中给定的 JSON 示例(略有修改)
For example the given JSON sample in the question (slightly modified)
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 类型与上述相同.还有一些额外的选项:
can be decoded into the struct Weather
. The Swift types are the same as described above. There are a few additional options:
URL
的字符串可以直接解码为URL
.time
整数可以用 dateDecodingStrategy
.secondsSince1970
解码为 Date
.keyDecodingStrategy
.convertFromSnakeCase
URL
can be decoded directly as URL
. time
integer can be decoded as Date
with the dateDecodingStrategy
.secondsSince1970
.keyDecodingStrategy
.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)
}
其他编码来源: