简体   繁体   中英

Parse Json array without keys swift 4

I am stuck on parsing JSON . The structure is really hard. I was trying this with a decodable approach.

import UIKit

struct WeatherItem: Decodable {
    let title: String?
    let value: String?
    let condition: String?
}

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        print("hello")

        let jsonUrlString = "http://virtualflight.ddns.net/api/weather.php?icao=ehrd"
        guard let url = URL(string: jsonUrlString) else { return }

        URLSession.shared.dataTask(with: url) { (data, response, err) in
            guard let data = data else { return }

            var arr = [WeatherItem]()
            do {
                let res = try JSONDecoder().decode([String:[[String]]].self, from: data)
                let content = res["title"]!
                content.forEach {
                    if $0.count >= 3 {
                        arr.append(WeatherItem(title:$0[0],value:$0[1],condition:$0[2]))
                    }
                }
                print(arr)
            } catch {
                print(error)
            }
        }
    }
}

The json is the following:

{
    "temperature": {
        "value_c": 11,
        "value_f": 285,
        "condition": "Good",
        "value_app": "11 \u00b0C (285 \u00b0F)"
    },
    "visibility": {
        "value_km": 10,
        "value_m": 6.2,
        "condition": "Good",
        "value_app": "10 KM (6.2 Mi)"
    },
    "pressure": {
        "value_hg": 29.4,
        "value_hpa": 996,
        "condition": "Good",
        "value_app": "29.4 inHg (996 hPa)"
    },
    "wind": {
        "value_kts": 20,
        "value_kmh": 37,
        "value_heading": 280,
        "condition": "Bad",
        "value_app": "280\u00b0 at 20 KTS (37 Km\/H)"
    },
    "station": "EHRD",
    "metar": "EHRD 141355Z AUTO 28020KT 250V320 9999 SCT038 BKN043 BKN048 11\/07 Q0996 NOSIG",
    "remarks": "NOSIG",
    "weather_page_ios_simple": [
        [
            "Temperature",
            "11 \u00b0C (285 \u00b0F)",
            "Good"
        ],
        [
            "Visibility",
            "10 KM (6.2 Mi)",
            "Good"
        ],
        [
            "Pressure",
            "29.4 inHg (996 hPa)",
            "Good"
        ],
        [
            "Wind",
            "280\u00b0 at 20 KTS (37 Km\/H)",
            "Bad"
        ],
        [
            "Metar",
            "EHRD 141355Z AUTO 28020KT 250V320 9999 SCT038 BKN043 BKN048 11\/07 Q0996 NOSIG",
            "Unknown"
        ],
        [
            "Remarks",
            "NOSIG",
            "Unknown"
        ],
        [
            "Station",
            "EHRD",
            "Unknown"
        ],
        [
            "UICell",
            "iOS 12",
            "siri_weather_cell"
        ]
    ]
}

any ideas how to do this?? I only need the last array, weather_page_ios_simple.

Have a look at https://app.quicktype.io it will give you the data structure for your JSON.

import Foundation

struct Welcome: Codable {
    let temperature: Temperature
    let visibility: Visibility
    let pressure: Pressure
    let wind: Wind
    let station, metar, remarks: String
    let weatherPageIosSimple: [[String]]

    enum CodingKeys: String, CodingKey {
        case temperature, visibility, pressure, wind, station, metar, remarks
        case weatherPageIosSimple = "weather_page_ios_simple"
    }
}

struct Pressure: Codable {
    let valueHg: Double
    let valueHpa: Int
    let condition, valueApp: String

    enum CodingKeys: String, CodingKey {
        case valueHg = "value_hg"
        case valueHpa = "value_hpa"
        case condition
        case valueApp = "value_app"
    }
}

struct Temperature: Codable {
    let valueC, valueF: Int
    let condition, valueApp: String

    enum CodingKeys: String, CodingKey {
        case valueC = "value_c"
        case valueF = "value_f"
        case condition
        case valueApp = "value_app"
    }
}

struct Visibility: Codable {
    let valueKM: Int
    let valueM: Double
    let condition, valueApp: String

    enum CodingKeys: String, CodingKey {
        case valueKM = "value_km"
        case valueM = "value_m"
        case condition
        case valueApp = "value_app"
    }
}

struct Wind: Codable {
    let valueKts, valueKmh, valueHeading: Int
    let condition, valueApp: String

    enum CodingKeys: String, CodingKey {
        case valueKts = "value_kts"
        case valueKmh = "value_kmh"
        case valueHeading = "value_heading"
        case condition
        case valueApp = "value_app"
    }
}

If you only need the bottom array of data, you shouldn't need to put everything into the decoded struct. Just decode the part of the response you want and pull the data from there. Also, that array of data isn't really parsing JSON without keys. Its just an array of strings, you'll have to rely on the fact that index 0 is always title, 1 is always value, and 2 is always condition. Just do some validation to make sure it fits your needs. Something like this (UNTESTED)

struct WeatherItem {

    let title: String?
    let value: String?
    let condition: String?

    init(title: String?, value: String?, condition: String?) {
        self.title = title
        self.value = value
        self.condition = condition
    }
}

struct WeatherResponse: Decodable {

    var weatherItems: [WeatherItem]

    private enum CodingKeys: String, CodingKey {
        case weatherItems = "weather_page_ios_simple"
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let weatherItemArrays = try container.decode([[String]].self, forKey: .weatherItems)

        weatherItems = []

        for weatherItemArray in weatherItemArrays {

            var title: String?

            if weatherItemArray.count > 0 {
                title = weatherItemArray[0]
            }

            var value: String?

            if weatherItemArray.count > 1 {
                value = weatherItemArray[1]
            }

            var condition: String?

            if weatherItemArray.count > 2 {
                condition = weatherItemArray[2]
            }

            weatherItems.append(WeatherItem(title: title, value: value, condition: condition))
        }
    }
}

And then when you get your api response get the weather items out with something like

do {
    let weatherResponse = try JSONDecoder().decode(WeatherResponse.self, from: <YOUR API RESPONSE DATA>)
    let weatherItems = weatherResponse.weatherItems

    <DO WHATEVER YOU WANT WITH THE WEATHER ITEMS>

} catch let error {
    print(error)
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM