Zu allererst nie Daten geladen werden synchron von einer Remote - URL , verwenden Sie immer asynchrone Methoden wie URLSession
.
'Any' hat keine tiefgestellten Mitglieder
tritt auf, weil der Compiler keine Ahnung hat, um welchen Typ es sich bei den Zwischenobjekten handelt (z. B. currently
in ["currently"]!["temperature"]
), und weil Sie Foundation-Auflistungstypen verwenden, wie NSDictionary
der Compiler überhaupt keine Ahnung vom Typ hat.
Zusätzlich ist es in Swift 3 erforderlich, den Compiler über den Typ aller tiefgestellten Objekte zu informieren .
Sie müssen das Ergebnis der JSON-Serialisierung in den tatsächlichen Typ umwandeln.
Dieser Code verwendet URLSession
und ausschließlich Swift native Typen
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()
Um alle Schlüssel / Wert-Paare von currentConditions
Ihnen zu drucken , könnten Sie schreiben
let currentConditions = parsedData["currently"] as! [String:Any]
for (key, value) in currentConditions {
print("\(key) - \(value) ")
}
Ein Hinweis zu jsonObject(with data
:
Viele (es scheint , alle) Anleitungen vorschlagen .mutableContainers
oder .mutableLeaves
Optionen , die in Swift vollständig ist Unsinn. Die beiden Optionen sind ältere Objective-C-Optionen zum Zuweisen des Ergebnisses zu NSMutable...
Objekten. In Swift ist jede var
iable standardmäßig veränderbar, und das Übergeben einer dieser Optionen und das Zuweisen des Ergebnisses zu einer let
Konstanten hat überhaupt keine Auswirkung. Außerdem mutieren die meisten Implementierungen den deserialisierten JSON sowieso nie.
Die einzige (selten) Option , die in Swift nützlich ist , ist .allowFragments
die , wenn erforderlich, wenn das JSON Wurzelobjekt ein Werttyp sein könnte ( String
, Number
, Bool
oder null
) , anstatt eine der Sammeltypen ( array
oder dictionary
). Aber normalerweise lassen Sie den options
Parameter weg , was bedeutet, dass keine Optionen verfügbar sind .
================================================== =========================
Einige allgemeine Überlegungen zum Parsen von JSON
JSON ist ein übersichtliches Textformat. Es ist sehr einfach, eine JSON-Zeichenfolge zu lesen. Lesen Sie die Zeichenfolge sorgfältig durch . Es gibt nur sechs verschiedene Typen - zwei Sammlungstypen und vier Werttypen.
Die Sammlungstypen sind
- Array - JSON: Objekte in eckigen Klammern
[]
- Swift: [Any]
aber in den meisten Fällen[[String:Any]]
- Wörterbuch - JSON: Objekte in geschweiften Klammern
{}
- Swift:[String:Any]
Die Werttypen sind
- String - JSON: Beliebiger Wert in doppelten Anführungszeichen
"Foo"
, auch "123"
oder "false"
- Swift:String
- Zahl - JSON: numerische Werte nicht in doppelten Anführungszeichen
123
oder 123.0
- Swift: Int
oderDouble
- Bool - JSON:
true
oder false
nicht in doppelten Anführungszeichen - Swift: true
oderfalse
- null - JSON:
null
- Swift:NSNull
Gemäß der JSON-Spezifikation müssen alle Schlüssel in Wörterbüchern sein String
.
Grundsätzlich wird immer empfohlen, optionale Bindungen zu verwenden, um Optionen sicher auszupacken
Wenn das Stammobjekt ein dictionary ( {}
) ist, wandeln Sie den Typ in um[String:Any]
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [String:Any] { ...
und Werte durch Schlüssel mit abrufen ( OneOfSupportedJSONTypes
ist entweder JSON-Sammlung oder Werttyp wie oben beschrieben.)
if let foo = parsedData["foo"] as? OneOfSupportedJSONTypes {
print(foo)
}
Wenn das Stammobjekt ein Array ( []
) ist, wandeln Sie den Typ in um[[String:Any]]
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]] { ...
und iteriere durch das Array mit
for item in parsedData {
print(item)
}
Wenn Sie ein Element an einem bestimmten Index benötigen, überprüfen Sie auch, ob der Index vorhanden ist
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]], parsedData.count > 2,
let item = parsedData[2] as? OneOfSupportedJSONTypes {
print(item)
}
}
In dem seltenen Fall, dass JSON einfach einer der Werttypen und nicht ein Auflistungstyp ist, müssen Sie die .allowFragments
Option übergeben und das Ergebnis beispielsweise in den entsprechenden Werttyp umwandeln
if let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? String { ...
Apple hat einen umfassenden Artikel im Swift-Blog veröffentlicht: Arbeiten mit JSON in Swift
================================================== =========================
In Swift 4+ Codable
bietet das Protokoll eine bequemere Möglichkeit, JSON direkt in Strukturen / Klassen zu analysieren.
Zum Beispiel das angegebene JSON-Beispiel in der Frage (leicht modifiziert)
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}
"""
kann in die Struktur dekodiert werden Weather
. Die Swift-Typen sind die gleichen wie oben beschrieben. Es gibt einige zusätzliche Optionen:
- Zeichenfolgen, die ein darstellen,
URL
können direkt als dekodiert werden URL
.
- Die
time
Ganzzahl kann wie Date
bei der dekodiert werden dateDecodingStrategy
.secondsSince1970
.
- snaked_cased JSON-Schlüssel können mit dem in camelCase konvertiert werden
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)
}
Andere codierbare Quellen: