Prawidłowe analizowanie JSON w Swift 3

123

Próbuję pobrać odpowiedź JSON i zapisać wyniki w zmiennej. Miałem wersje tego kodu działające w poprzednich wersjach Swift, aż do wydania wersji GM Xcode 8. Rzuciłem okiem na kilka podobnych postów na StackOverflow: Swift 2 Parsing JSON - Nie można indeksować wartości typu `` AnyObject '' i JSON Parsing w Swift 3 .

Jednak wydaje się, że przedstawione tam pomysły nie mają zastosowania w tym scenariuszu.

Jak poprawnie przeanalizować odpowiedź JSON w Swift 3? Czy coś się zmieniło w sposobie odczytywania JSON w Swift 3?

Poniżej znajduje się kod, o którym mowa (można go uruchomić na placu zabaw):

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)")
        }
    }
}

Edycja: oto przykład wyników wywołania API poprint(currentConditions)

["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]
user2563039
źródło
Czy możesz umieścić przykładowe dane zwrócone przez wywołanie interfejsu API?
Użytkownik
1
Tak, właśnie dodałem próbkę wyników wydrukowanych po wydrukowaniu (currentConditions). Mam nadzieję, że to pomoże.
user2563039
Przeanalizuj json w swift4 przy użyciu protokołu Codable stackoverflow.com/a/52931265/9316566
Naser Mohamed

Odpowiedzi:

172

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 currentlyw ["currently"]!["temperature"]), a ponieważ używasz typów kolekcji Foundation, tak jak NSDictionarykompilator 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 URLSessioni 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ść, currentConditionsmoż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 .mutableContainerslub .mutableLeavesopcji, 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 variable jest domyślnie zmienny i przekazanie którejkolwiek z tych opcji i przypisanie wyniku do letstał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 .allowFragmentsktóry jest wymagany w przypadku, gdy głównym celem JSON może być typu wartość ( String, Number, Booli null), niż jeden z typów Collection ( arraylub dictionary). Ale zwykle pomijaj optionsparametr, 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 123lub 123.0- Swift: IntlubDouble
  • Bool - JSON: truelub false nie w cudzysłowach - Swift: truelubfalse
  • 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 ( OneOfSupportedJSONTypesjest 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ć .allowFragmentsopcję 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+ Codableprotokół 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 URLmożna dekodować bezpośrednio jako URL.
  • timeCałkowita może być dekodowany Datez dateDecodingStrategy .secondsSince1970.
  • Klucze JSON snaked_cased można przekonwertować na camelCase za pomocą rozszerzeniakeyDecodingStrategy .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:

vadian
źródło
To jest bardzo pomocne. Jestem po prostu ciekawy, dlaczego powyższy kod nie pokazuje widocznych danych wyjściowych, gdy jest uruchamiany na placu zabaw.
user2563039
Jak wspomniano powyżej, musisz rzutować niejednoznaczny wynik funkcji dict!["currently"]!na słownik, z którego kompilator może bezpiecznie wywnioskować kolejną subskrypcję klucza.
vadian,
1
Artykuł Apple jest całkiem fajny, ale z jakiegoś powodu nie mogłem go uruchomić ze Swift 3. Narzeka na Type [String: Any]? nie ma członków indeksu dolnego. Było z tym też kilka innych problemów, ale udało mi się to obejść. Czy ktoś ma przykład swojego kodu, który faktycznie działa?
Shades
@Shades Zadaj pytanie i opublikuj swój kod. Najprawdopodobniej problem jest związany z nieopakowanymi opcjami.
Vadian
Czy możesz umieścić przykładowe dane zwrócone przez wywołanie interfejsu API?
Użytkownik
12

Dużą zmianą, jaka nastąpiła w Xcode 8 Beta 6 dla Swift 3, było to, że id teraz importuje jako Anyzamiast AnyObject.

Oznacza to, że parsedDatajest zwracany jako słownik najprawdopodobniej z typem [Any:Any]. Bez debugera nie mogłem dokładnie powiedzieć, co NSDictionaryzrobi twoje rzutowanie , ale błąd, który widzisz, jest taki, że dict!["currently"]!ma typAny

Jak więc to rozwiązać? Ze sposobu, w jaki się do niego odwołałeś, zakładam, że dict!["currently"]!jest to słownik, więc masz wiele opcji:

Najpierw możesz zrobić coś takiego:

let currentConditionsDictionary: [String: AnyObject] = dict!["currently"]! as! [String: AnyObject]  

W ten sposób otrzymasz obiekt słownikowy, do którego możesz następnie zapytać o wartości, dzięki czemu uzyskasz swoją temperaturę w następujący sposób:

let currentTemperatureF = currentConditionsDictionary["temperature"] as! Double

Lub jeśli wolisz, możesz to zrobić w kolejce:

let currentTemperatureF = (dict!["currently"]! as! [String: AnyObject])["temperature"]! as! Double

Mam nadzieję, że to pomoże, obawiam się, że nie miałem czasu napisać przykładowej aplikacji, aby ją przetestować.

Ostatnia uwaga: najłatwiejszą rzeczą do zrobienia może być po prostu przerzucenie ładunku JSON [String: AnyObject]na samym początku.

let parsedData = try JSONSerialization.jsonObject(with: data as Data, options: .allowFragments) as! Dictionary<String, AnyObject>
discorevilo
źródło
dict!["currently"]! as! [String: String]się rozbije
vadian
Punktem, w którym się rozbił, była [„temperatura”] !! a także próbuję rzucić String na NSString - zweryfikowano, że nowe rozwiązanie działa swiftlang.ng.bluemix.net/#/repl/57d3bc683a422409bf36c391
discorevilo
[String: String]nie może w ogóle działać, ponieważ istnieje kilka wartości liczbowych. To nie działa na placu zabaw dla komputerów Mac
vadian,
@vadian ah, tak, nie zauważyłem ich, a szybka piaskownica pomogła je ukryć! - Poprawione [ponownie] teraz (i przetestowane na macOS). Dziękuję za zwrócenie uwagi :)
discorevilo
6
let str = "{\"names\": [\"Bob\", \"Tim\", \"Tina\"]}"

let data = str.data(using: String.Encoding.utf8, allowLossyConversion: false)!

do {
    let json = try JSONSerialization.jsonObject(with: data, options: []) as! [String: AnyObject]
    if let names = json["names"] as? [String] 
{
        print(names)
}
} catch let error as NSError {
    print("Failed to load: \(error.localizedDescription)")
}
BhuShan PaWar
źródło
5

Właśnie w tym celu zbudowałem quicktype . Po prostu wklej przykładowy kod JSON, a quicktype wygeneruje następującą hierarchię typów dla danych API:

struct Forecast {
    let hourly: Hourly
    let daily: Daily
    let currently: Currently
    let flags: Flags
    let longitude: Double
    let latitude: Double
    let offset: Int
    let timezone: String
}

struct Hourly {
    let icon: String
    let data: [Currently]
    let summary: String
}

struct Daily {
    let icon: String
    let data: [Datum]
    let summary: String
}

struct Datum {
    let precipIntensityMax: Double
    let apparentTemperatureMinTime: Int
    let apparentTemperatureLowTime: Int
    let apparentTemperatureHighTime: Int
    let apparentTemperatureHigh: Double
    let apparentTemperatureLow: Double
    let apparentTemperatureMaxTime: Int
    let apparentTemperatureMax: Double
    let apparentTemperatureMin: Double
    let icon: String
    let dewPoint: Double
    let cloudCover: Double
    let humidity: Double
    let ozone: Double
    let moonPhase: Double
    let precipIntensity: Double
    let temperatureHigh: Double
    let pressure: Double
    let precipProbability: Double
    let precipIntensityMaxTime: Int
    let precipType: String?
    let sunriseTime: Int
    let summary: String
    let sunsetTime: Int
    let temperatureMax: Double
    let time: Int
    let temperatureLow: Double
    let temperatureHighTime: Int
    let temperatureLowTime: Int
    let temperatureMin: Double
    let temperatureMaxTime: Int
    let temperatureMinTime: Int
    let uvIndexTime: Int
    let windGust: Double
    let uvIndex: Int
    let windBearing: Int
    let windGustTime: Int
    let windSpeed: Double
}

struct Currently {
    let precipProbability: Double
    let humidity: Double
    let cloudCover: Double
    let apparentTemperature: Double
    let dewPoint: Double
    let ozone: Double
    let icon: String
    let precipIntensity: Double
    let temperature: Double
    let pressure: Double
    let precipType: String?
    let summary: String
    let uvIndex: Int
    let windGust: Double
    let time: Int
    let windBearing: Int
    let windSpeed: Double
}

struct Flags {
    let sources: [String]
    let isdStations: [String]
    let units: String
}

Generuje również kod organizacyjny bez zależności, aby przekonać wartość zwracaną JSONSerialization.jsonObjectdo a Forecast, w tym wygodny konstruktor, który przyjmuje ciąg JSON, dzięki czemu można szybko przeanalizować silnie wpisaną Forecastwartość i uzyskać dostęp do jej pól:

let forecast = Forecast.from(json: jsonString)!
print(forecast.daily.data[0].windGustTime)

Możesz zainstalować quicktype z npm za pomocą interfejsu internetowegonpm i -g quicktype lub użyć interfejsu internetowego, aby uzyskać kompletny wygenerowany kod do wklejenia na swoim placu zabaw.

David Siegel
źródło
Linki nie działają.
zwrócił ..
Fantastyczna oszczędność czasu! Dobra robota!
marko
1
Genialne narzędzie. Dzięki.
Ahmet Ardal
4

Zaktualizowany się isConnectToNetwork-Functionpotem, dzięki tym poście .

Napisałem na to dodatkową metodę:

import SystemConfiguration

func loadingJSON(_ link:String, postString:String, completionHandler: @escaping (_ JSONObject: AnyObject) -> ()) {

    if(isConnectedToNetwork() == false){
        completionHandler("-1" as AnyObject)
        return
    }

    let request = NSMutableURLRequest(url: URL(string: link)!)
    request.httpMethod = "POST"
    request.httpBody = postString.data(using: String.Encoding.utf8)

    let task = URLSession.shared.dataTask(with: request as URLRequest) { data, response, error in
        guard error == nil && data != nil else { // check for fundamental networking error
            print("error=\(error)")
            return
        }

        if let httpStatus = response as? HTTPURLResponse , httpStatus.statusCode != 200 { // check for http errors
            print("statusCode should be 200, but is \(httpStatus.statusCode)")
            print("response = \(response)")
        }
        //JSON successfull
        do {
            let parseJSON = try JSONSerialization.jsonObject(with: data!, options: .allowFragments)
            DispatchQueue.main.async(execute: {
                completionHandler(parseJSON as AnyObject)
            });
        } catch let error as NSError {
            print("Failed to load: \(error.localizedDescription)")
        }
    }
    task.resume()
}

func isConnectedToNetwork() -> Bool {

    var zeroAddress = sockaddr_in(sin_len: 0, sin_family: 0, sin_port: 0, sin_addr: in_addr(s_addr: 0), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
    zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
    zeroAddress.sin_family = sa_family_t(AF_INET)

    let defaultRouteReachability = withUnsafePointer(to: &zeroAddress) {
        $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {zeroSockAddress in
            SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress)
        }
    }

    var flags: SCNetworkReachabilityFlags = SCNetworkReachabilityFlags(rawValue: 0)
    if SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) == false {
        return false
    }

    let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
    let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
    let ret = (isReachable && !needsConnection)

    return ret
}

Teraz możesz łatwo zadzwonić do tego w swojej aplikacji, gdziekolwiek chcesz

loadingJSON("yourDomain.com/login.php", postString:"email=\(userEmail!)&password=\(password!)") { parseJSON in

    if(String(describing: parseJSON) == "-1"){
        print("No Internet")
    } else {

    if let loginSuccessfull = parseJSON["loginSuccessfull"] as? Bool {
        //... do stuff
    }
}
Marco Weber
źródło
Marco, jaki byłby najlepszy sposób na dodanie tej funkcji do projektu? w jakimkolwiek contorllerze lub modelu?
KamalPanhwar
hej, po prostu dodaj pierwszą metodę func loadingJSON (...) w dodatkowym pliku lub klasie szybkiego dostępu. Po tym możesz zadzwonić z każdego kontrolera w swoim projekcie
Marco Weber
Próbowałem, ale uwielbiam pomysł demonstracji pełnego rozwiązania, a także tego, jak go używać, w tym metody pomocniczej isConnectedToNetwork (), to mi jest pomysł, jak zaimplementować to prawdopodobnie w dobrym kodzie
Amr Angry
więc właśnie użyłem nowego pliku swift (lewy przycisk myszy na drzewie projektu, nowy plik ..., plik swift) i nazwałem go jsonhelper.swift. w tym pliku umieszczasz pierwszy kod, ładując JSON () i isConnectedToNetwork (). potem możesz użyć tych dwóch funkcji w każdej części swojego projektu. na przykład w loginVC, jako akcja przycisku logowania, możesz użyć drugiego kodu, oczywiście musisz zmienić domenę, ciąg znaków i wartości paseJson (parseJSON ["loginSuccessfull"]), aby pasowały do twój plik php
Marco Weber
0

Swift ma potężne wnioskowanie o typie. Pozbądźmy się schematu „if let” lub „guard let” i wymuś rozpinanie używając podejścia funkcjonalnego:

  1. Oto nasz JSON. Możemy użyć opcjonalnego JSON lub zwykłego. W naszym przykładzie używam opcjonalnego:

    let json: Dictionary<String, Any>? = ["current": ["temperature": 10]]
  1. Funkcje pomocnicze. Musimy je napisać tylko raz, a następnie użyć ponownie w dowolnym słowniku:

    /// Curry
    public func curry<A, B, C>(_ f: @escaping (A, B) -> C) -> (A) -> (B) -> C {
        return { a in
            { f(a, $0) }
        }
    }

    /// Function that takes key and optional dictionary and returns optional value
    public func extract<Key, Value>(_ key: Key, _ json: Dictionary<Key, Any>?) -> Value? {
        return json.flatMap {
            cast($0[key])
        }
    }

    /// Function that takes key and return function that takes optional dictionary and returns optional value
    public func extract<Key, Value>(_ key: Key) -> (Dictionary<Key, Any>?) -> Value? {
        return curry(extract)(key)
    }

    /// Precedence group for our operator
    precedencegroup RightApplyPrecedence {
        associativity: right
        higherThan: AssignmentPrecedence
        lowerThan: TernaryPrecedence
    }

    /// Apply. g § f § a === g(f(a))
    infix operator § : RightApplyPrecedence
    public func §<A, B>(_ f: (A) -> B, _ a: A) -> B {
        return f(a)
    }

    /// Wrapper around operator "as".
    public func cast<A, B>(_ a: A) -> B? {
        return a as? B
    }
  1. A oto nasza magia - wyodrębnij wartość:

    let temperature = (extract("temperature") § extract("current") § json) ?? NSNotFound

Tylko jedna linia kodu i bez wymuszania rozpakowywania lub ręcznego rzutowania typu. Ten kod działa na placu zabaw, więc możesz go skopiować i sprawdzić. Oto implementacja w GitHub.

J. Doe
źródło
0

To jest inny sposób rozwiązania problemu. Sprawdź więc poniższe rozwiązanie. Mam nadzieję, że to ci pomoże.

let str = "{\"names\": [\"Bob\", \"Tim\", \"Tina\"]}"
let data = str.data(using: String.Encoding.utf8, allowLossyConversion: false)!
do {
    let json = try JSONSerialization.jsonObject(with: data, options: []) as! [String: AnyObject]
    if let names = json["names"] as? [String] {
        print(names)
    }
} catch let error as NSError {
    print("Failed to load: \(error.localizedDescription)")
}
Olcay Ertaş
źródło
0

Problem dotyczy metody interakcji API. Analizowanie JSON zmienia się tylko w składni. Główny problem tkwi w sposobie pobierania danych. To, czego używasz, to synchroniczny sposób pobierania danych. To nie działa w każdym przypadku. Powinieneś używać asynchronicznego sposobu pobierania danych. W ten sposób musisz zażądać danych przez API i poczekać, aż dane odpowiedzą. Możesz to osiągnąć za pomocą sesji URL i bibliotek zewnętrznych, takich jak Alamofire. Poniżej znajduje się kod metody sesji URL.

let urlString = "https://api.forecast.io/forecast/apiKey/37.5673776,122.048951"
let url = URL.init(string: urlString)
URLSession.shared.dataTask(with:url!) { (data, response, error) in
    guard error == nil else {
        print(error)
    }
    do {
        let Data = try JSONSerialization.jsonObject(with: data!) as! [String:Any]
        // Note if your data is coming in Array you should be using [Any]()
        //Now your data is parsed in Data variable and you can use it normally
        let currentConditions = Data["currently"] as! [String:Any]
        print(currentConditions)
        let currentTemperatureF = currentConditions["temperature"] as! Double
        print(currentTemperatureF)
    } catch let error as NSError {
        print(error)
    }
}.resume()
Arun K.
źródło
0
{
    "User":[
      {
        "FirstUser":{
        "name":"John"
        },
       "Information":"XY",
        "SecondUser":{
        "name":"Tom"
      }
     }
   ]
}

Jeśli utworzę model przy użyciu poprzedniego json Używając tego linku [blog]: http://www.jsoncafe.com do generowania struktury kodowalnej lub dowolnego formatu

Model

import Foundation
struct RootClass : Codable {
    let user : [Users]?
    enum CodingKeys: String, CodingKey {
        case user = "User"
    }

    init(from decoder: Decoder) throws {
        let values = try? decoder.container(keyedBy: CodingKeys.self)
        user = try? values?.decodeIfPresent([Users].self, forKey: .user)
    }
}

struct Users : Codable {
    let firstUser : FirstUser?
    let information : String?
    let secondUser : SecondUser?
    enum CodingKeys: String, CodingKey {
        case firstUser = "FirstUser"
        case information = "Information"
        case secondUser = "SecondUser"
    }
    init(from decoder: Decoder) throws {
        let values = try? decoder.container(keyedBy: CodingKeys.self)
        firstUser = try? FirstUser(from: decoder)
        information = try? values?.decodeIfPresent(String.self, forKey: .information)
        secondUser = try? SecondUser(from: decoder)
    }
}
struct SecondUser : Codable {
    let name : String?
    enum CodingKeys: String, CodingKey {
        case name = "name"
    }
    init(from decoder: Decoder) throws {
        let values = try? decoder.container(keyedBy: CodingKeys.self)
        name = try? values?.decodeIfPresent(String.self, forKey: .name)
    }
}
struct FirstUser : Codable {
    let name : String?
    enum CodingKeys: String, CodingKey {
        case name = "name"
    }
    init(from decoder: Decoder) throws {
        let values = try? decoder.container(keyedBy: CodingKeys.self)
        name = try? values?.decodeIfPresent(String.self, forKey: .name)
    }
}

Analizować

    do {
        let res = try JSONDecoder().decode(RootClass.self, from: data)
        print(res?.user?.first?.firstUser?.name ?? "Yours optional value")
    } catch {
        print(error)
    }
Abishek T
źródło