Przede wszystkim nigdy nie ładuj danych synchronicznie ze zdalnego adresu URL , używaj zawsze metod asynchronicznych, takich jak URLSession
.
„Any” nie ma elementów indeksu dolnego
występuje, ponieważ kompilator nie ma pojęcia, jakiego typu są obiekty pośrednie (na przykład currently
w ["currently"]!["temperature"]
), a ponieważ używasz typów kolekcji Foundation, tak jak NSDictionary
kompilator nie ma żadnego pojęcia o typie.
Dodatkowo w Swift 3 wymagane jest poinformowanie kompilatora o typie wszystkich obiektów z indeksem.
Musisz rzutować wynik serializacji JSON na rzeczywisty typ.
Ten kod używa URLSession
i wyłącznie typów natywnych 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()
Aby wydrukować wszystkie pary klucz / wartość, currentConditions
możesz napisać
let currentConditions = parsedData["currently"] as! [String:Any]
for (key, value) in currentConditions {
print("\(key) - \(value) ")
}
Uwaga dotycząca jsonObject(with data
:
Wiele (wydaje się, że wszystkie) samouczków sugeruje .mutableContainers
lub .mutableLeaves
opcji, co w Swift jest całkowicie nonsensowne. Dwie opcje to starsze opcje Objective-C, służące do przypisywania wyniku do NSMutable...
obiektów. W Swift każdy var
iable jest domyślnie zmienny i przekazanie którejkolwiek z tych opcji i przypisanie wyniku do let
stałej nie ma żadnego efektu. Co więcej, większość implementacji i tak nigdy nie mutuje zdeserializowanego JSON.
Jedyny (rzadki) rozwiązaniem, które jest przydatne w SWIFT .allowFragments
który jest wymagany w przypadku, gdy głównym celem JSON może być typu wartość ( String
, Number
, Bool
i null
), niż jeden z typów Collection ( array
lub dictionary
). Ale zwykle pomijaj options
parametr, co oznacza brak opcji .
==================================================== =========================
Kilka ogólnych uwag dotyczących analizowania formatu JSON
JSON to dobrze zorganizowany format tekstowy. Bardzo łatwo jest odczytać ciąg JSON. Przeczytaj uważnie tekst . Istnieje tylko sześć różnych typów - dwa typy kolekcji i cztery typy wartości.
Typy kolekcji to
- Tablica - JSON: obiekty w nawiasach kwadratowych
[]
- Swift: [Any]
ale w większości przypadków[[String:Any]]
- Słownik - JSON: obiekty w nawiasach klamrowych
{}
- Swift:[String:Any]
Typy wartości to
- Ciąg znaków - JSON: dowolna wartość w podwójnych cudzysłowach
"Foo"
, parzysta "123"
lub "false"
- Swift:String
- Liczba - JSON: wartości liczbowe bez podwójnych cudzysłowów
123
lub 123.0
- Swift: Int
lubDouble
- Bool - JSON:
true
lub false
nie w cudzysłowach - Swift: true
lubfalse
- null - JSON:
null
- Swift:NSNull
Zgodnie ze specyfikacją JSON wszystkie klucze w słownikach muszą być String
.
Zasadniczo zawsze zaleca się używanie opcjonalnych powiązań do bezpiecznego rozpakowywania opcji
Jeśli obiekt główny jest Dictionary ( {}
) rzutem na typ[String:Any]
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [String:Any] { ...
i pobieraj wartości za pomocą kluczy z ( OneOfSupportedJSONTypes
jest to kolekcja JSON lub typ wartości, jak opisano powyżej).
if let foo = parsedData["foo"] as? OneOfSupportedJSONTypes {
print(foo)
}
Jeśli obiekt główny jest tablicą ( []
), rzut na typ[[String:Any]]
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]] { ...
i iteruj po tablicy za pomocą
for item in parsedData {
print(item)
}
Jeśli potrzebujesz elementu o określonym indeksie, sprawdź również, czy indeks istnieje
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]], parsedData.count > 2,
let item = parsedData[2] as? OneOfSupportedJSONTypes {
print(item)
}
}
W rzadkich przypadkach, gdy JSON jest po prostu jednym z typów wartości - a nie typem kolekcji - musisz przekazać .allowFragments
opcję i na przykład rzutować wynik na odpowiedni typ wartości
if let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? String { ...
Firma Apple opublikowała obszerny artykuł na swoim blogu Swift: Praca z JSON w Swift
==================================================== =========================
W Swift 4+ Codable
protokół zapewnia wygodniejszy sposób analizowania JSON bezpośrednio na struktury / klasy.
Na przykład podana próbka JSON w pytaniu (nieznacznie zmodyfikowana)
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}
"""
można zdekodować do struktury Weather
. Typy jerzyków są takie same, jak opisano powyżej. Istnieje kilka dodatkowych opcji:
- Ciągi reprezentujące an
URL
można dekodować bezpośrednio jako URL
.
time
Całkowita może być dekodowany Date
z dateDecodingStrategy
.secondsSince1970
.
- Klucze JSON snaked_cased można przekonwertować na camelCase za pomocą rozszerzenia
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)
}
Inne kodowane źródła: