Home > database >  How to build a model from a JSON file
How to build a model from a JSON file

Time:11-18

This is my first time working with JSON in Swift and when i'm trying to parse the file with my model, this error appears:

The given data was not valid JSON.

I think the problem lies with how I do my model.

The Way I parse the JSON:

import SwiftUI

struct EmergencyView: View {
    let emergency: [EmergencyNumberModel]
    init() {
        let url = Bundle.main.url(forResource: "emergency",
                                  withExtension: "json")!
        let data = try! Data(contentsOf: url)
        emergency = try! JSONDecoder().decode([EmergencyNumberModel].self, from: data) //Error
    }
    
    var body: some View {
        List(emergency, id: \.id) { emer in
            
            if emer.Country != nil {
                Label(emer.Country, systemImage: "quote.bubble")
                    .font(.headline)
            } else{
                Text(emer.Country)
            }
        }
        navigationTitle("Emergency")
    }
}

This is a fraction of the JSON i'm using, "emergency.json":

[
  {
    "Country": {
      "Name": "Afghanistan",
      "ISOCode": "AF",
      "ISONumeric": "4"
    },
    "Ambulance": {
      "All": [
        "112"
      ]
    },
    "Fire": {
      "All": [
        "119"
      ]
    },
    "Police": {
      "All": [
        "119"
      ]
    },
    "Dispatch": {
      "All": [
        null
      ]
    },
    "Member_112": false,
    "LocalOnly": true,
    "Notes": false
  },
 
 .
 .
 .

]

This is my Model, "EmergencyNumberModel.swift":

struct EmergencyNumberModel: Codable, Identifiable {
    var id = UUID()
    let Country: String
    let Ambulance: String
    let Fire: String
    let Police: String
    let Dispatch: String
}

Do I need other variables in my model to access the inner keys or the data types of the variables are wrong?

CodePudding user response:

Using https://app.quicktype.io/, to generate the swift strutures, this is one basic approach to use your json data:

struct EmergencyView: View {
    @State var emergencies: [EmergencyModel] = []  // <--- here
    
    var body: some View {
        List(emergencies) { emer in
            if emer.country.name.isEmpty {
                Text("no country name")
            } else {
                Label(emer.country.name, systemImage: "quote.bubble").font(.headline)
            }
        }
        .onAppear {
            if let emrgncy = loadData(from: "emergency") {  // <--- here
                emergencies = emrgncy
            }
        }
    }
    
    func loadData(from file: String) -> [EmergencyModel]? {
        do {
            if let filePath = Bundle.main.path(forResource: file, ofType: "json") {
                let data = try Data(contentsOf: URL(fileURLWithPath: filePath))
                let results = try JSONDecoder().decode([EmergencyModel].self, from: data)
                return results
            }
        } catch {
            print("----> error: \(error)") // <-- todo, deal with errors
        }
        return nil
    }
    
}

struct EmergencyModel: Identifiable, Codable {
    let id = UUID()  // <--- here
    let country: Country
    let ambulance, fire, police: Ambulance
    let dispatch: Dispatch
    let member112, localOnly, notes: Bool
    
    enum CodingKeys: String, CodingKey {
        case country = "Country"
        case ambulance = "Ambulance"
        case fire = "Fire"
        case police = "Police"
        case dispatch = "Dispatch"
        case member112 = "Member_112"
        case localOnly = "LocalOnly"
        case notes = "Notes"
    }
}

struct Ambulance: Codable {
    let all: [String]
    
    enum CodingKeys: String, CodingKey {
        case all = "All"
    }
}

struct Country: Codable {
    let name, isoCode, isoNumeric: String
    
    enum CodingKeys: String, CodingKey {
        case name = "Name"
        case isoCode = "ISOCode"
        case isoNumeric = "ISONumeric"
    }
}

struct Dispatch: Codable {
    let all: [JSONNull?]
    
    enum CodingKeys: String, CodingKey {
        case all = "All"
    }
}

class JSONNull: Codable, Hashable {
    
    public static func == (lhs: JSONNull, rhs: JSONNull) -> Bool {
        return true
    }
    
    func hash(into hasher: inout Hasher) {
       hasher.combine(0)
    }
    
    public init() {}
    
    public required init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if !container.decodeNil() {
            throw DecodingError.typeMismatch(JSONNull.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JSONNull"))
        }
    }
    
    public func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encodeNil()
    }
}
  • Related