Home > front end >  Error decoding: typeMismatch , In decoding Data from a Rest API (VBB)
Error decoding: typeMismatch , In decoding Data from a Rest API (VBB)

Time:01-24

I am trying to retrieve the JSON from the Rest API using this method.


import Foundation
import Combine
import SwiftUI

class JsonDataVBB: ObservableObject {
@Published var stationNameAPI = [StopLocation]()

    func departureNEWOldFetch() {
         let url = URL(string: "https://....")!
         URLSession.shared.dataTask(with: url) { (data, response, error) in
             if let error = error {
                 print("Error fetching departures: \(error)")
                 return
             }
             guard let data = data else {
                 print("No data received")
                 return
             }
             do {
                 let sucheVBB = try JSONDecoder().decode([StopLocation].self, from: data)
                 DispatchQueue.main.async {
                     self.stationNameAPI = sucheVBB
    
                 }
             } catch let error {
                 print("Error decoding departures: \(error)")
             }
         }.resume()
     }

}

For this I created a Swift Decoding Model, with which I decode the Json: (with app.quicktype...)


import Foundation


// MARK: - StationLocal
struct StationSuche: Codable, Identifiable {
    var id = UUID()
    let stopLocationOrCoordLocation: [StopLocationOrCoordLocation]
    let technicalMessages: TechnicalMessages
    let serverVersion, dialectVersion, requestID: String

    enum CodingKeys: String, CodingKey {
        case stopLocationOrCoordLocation
        case technicalMessages
        case serverVersion, dialectVersion
        case requestID
    }
}

// MARK: - StopLocationOrCoordLocation
struct StopLocationOrCoordLocation: Codable {
    let stopLocation: StopLocation

    enum CodingKeys: String, CodingKey {
        case stopLocation
    }
}

// MARK: - StopLocation
struct StopLocation: Codable {
    let locationNotes: LocationNotes
    let altID: [String]
    let timezoneOffset: Int
    let id, extID, name: String
    let lon, lat: Double
    let weight, products: Int

    enum CodingKeys: String, CodingKey {
        case locationNotes
        case altID
        case timezoneOffset, id
        case extID
        case name, lon, lat, weight, products
    }
}

// MARK: - LocationNotes
struct LocationNotes: Codable {
    let locationNote: [LocationNote]

    enum CodingKeys: String, CodingKey {
        case locationNote
    }
}

// MARK: - LocationNote
struct LocationNote: Codable {
    let value, key, type, txtN: String
}

// MARK: - TechnicalMessages
struct TechnicalMessages: Codable {
    let technicalMessage: [TechnicalMessage]

    enum CodingKeys: String, CodingKey {
        case technicalMessage
    }
}

// MARK: - TechnicalMessage
struct TechnicalMessage: Codable {
    let value, key: String
}


I make the query in my view:


import SwiftUI


struct TestTABTESTTEST: View {
    @ObservedObject var jsonDataVBB = JsonDataVBB()
    var body: some View {
        NavigationView {
            VStack {
                List {
             //(here i need help also, with for each)
                }
                .navigationBarTitle("⚠", displayMode: .inline)
                .onAppear{
                    jsonDataVBB.departureNEWOldFetch()
                }
                .refreshable {
                    jsonDataVBB.departureNEWOldFetch()
                }
            }
            .toolbarBackground(.visible, for: .navigationBar)

            
        }
        .toolbarBackground(.visible, for: .navigationBar)
        .toolbarBackground(.visible, for: .tabBar)
        .toolbarBackground(.visible, for: .bottomBar)
        .toolbarBackground(.visible, for: .navigationBar)
        
    }
}
struct TestTABTESTTEST_Previews: PreviewProvider {
    static var previews: some View {
        TestTABTESTTEST()
    }
}

My problem is that whenever I display the view and thus also execute the query I get the error:


Error decoding departures: typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil))

Here I have the JSON file (what comes back from the server)


{
  "stopLocationOrCoordLocation": [
    {
      "StopLocation": {
        "LocationNotes": {
          "LocationNote": [
            {
              "value": "Rolltreppe",
              "key": "FT",
              "type": "A",
              "txtN": "Rolltreppe"
            },
            {
              "value": "Blindenleitstreifen außer auf Bahnsteig U8",
              "key": "Bw",
              "type": "A",
              "txtN": "Blindenleitstreifen außer auf Bahnsteig U8"
            },
            {
              "value": "Aufzug",
              "key": "AT",
              "type": "A",
              "txtN": "Aufzug"
            },
            {
              "value": "de:11000:900100003",
              "key": "IF",
              "type": "I",
              "txtN": "de:11000:900100003"
            },
            {
              "value": "8089001",
              "key": "TD",
              "type": "I",
              "txtN": "8089001"
            },
            {
              "value": "8003135",
              "key": "TE",
              "type": "I",
              "txtN": "8003135"
            },
            {
              "value": "900100003 5555 Berlin AB % S U Alexanderplatz Bhf (Berlin)",
              "key": "TW",
              "type": "I",
              "txtN": "900100003 5555 Berlin AB % S U Alexanderplatz Bhf (Berlin)"
            }
          ]
        },
        "altId": [
          "de:11000:900100003"
        ],
        "timezoneOffset": 60,
        "id": "A=1@O=S U Alexanderplatz Bhf (Berlin)@X=13411267@Y=52521508@U=86@L=900100003@B=1@p=1674132395@",
        "extId": "900100003",
        "name": "S U Alexanderplatz Bhf (Berlin)",
        "lon": 13.411267,
        "lat": 52.521508,
        "weight": 10276,
        "products": 79
      }
    }
  ],
  "TechnicalMessages": {
    "TechnicalMessage": [
      {
        "value": "2023-01-24 01:18:08",
        "key": "requestTime"
      },
      {
        "value": "ttp=15721#16049 plancode0=y0q3x planid=1674132395 planid0=1674132395 planid_adr=1592347662 plancode_adr=mbtls planid_poi=1674132375 plancode_poi=y0q3d planid_ext=1516270147 plancode_ext=c07th srvv=5.45.VBB.13.3.12 (customer/hcuvbb/release/2023.01.0) [Jan 17 2023] tlibv=TRFVER: rel/vbb/5.04.0 2022-10-05 15:14:12  0200 VBB_TARIF   Relay_EnrichmentProxy v1 jno=2",
        "key": "backendInfo"
      }
    ]
  },
  "serverVersion": "2.32.1",
  "dialectVersion": "2.32",
  "requestId": "4pwxzkiwk24qymcs"
}

I have already looked at various tutorials, and have also played a bit with ChatGPT, I'm still pretty new with decode and Json in Swift ui, with local (in Bundle.main....) Json I've come so far quite well and also with light queries, but here I do me very difficult, I have now decided after eternal fiddling just to ask here in the community.

I have also read through other posts but unfortunately they have not been able to help me ...

CodePudding user response:

try this example code to fetch, decode and display your json data. The important thing to remember is that your models need to match the json data exactly.

You will have to consult the server docs to determine which properties are optional, and adjust the code of the models by adding ? to it.

struct ContentView: View {
    var body: some View {
        TestTABTESTTEST()
    }
}

struct TestTABTESTTEST: View {
    @StateObject var jsonDataVBB = JsonDataVBB()  // <-- here
    
    var body: some View {
        NavigationView {
            VStack {
                // --- here
                List(jsonDataVBB.stationNameAPI) { item in
                    Text(item.name).foregroundColor(.blue)
                    Text("lat: \(item.lat)  lon: \(item.lon)")
                }
                .navigationBarTitle("⚠", displayMode: .inline)
                .onAppear{
                    jsonDataVBB.departureNEWOldFetch()
                }
                .refreshable {
                    jsonDataVBB.departureNEWOldFetch()
                }
            }
            .toolbarBackground(.visible, for: .navigationBar)
        }
        .toolbarBackground(.visible, for: .navigationBar)
        .toolbarBackground(.visible, for: .tabBar)
        .toolbarBackground(.visible, for: .bottomBar)
        .toolbarBackground(.visible, for: .navigationBar)
    }
}

class JsonDataVBB: ObservableObject {
    @Published var stationNameAPI = [StopLocation]()
    
    func departureNEWOldFetch() {
        let url = URL(string: "https://....")!
        URLSession.shared.dataTask(with: url) { (data, response, error) in
            if let error = error {
                print("Error fetching departures: \(error)")
                return
            }
            guard let data = data else {
                print("No data received")
                return
            }
            do {
                // --- here
                let sucheVBB = try JSONDecoder().decode(StationSuche.self, from: data)
                DispatchQueue.main.async {
                    // --- here
                    self.stationNameAPI = sucheVBB.stopLocationOrCoordLocation.map{$0.stopLocation}
                }
            } catch let error {
                print("Error decoding departures: \(error)")
            }
        }.resume()
    }
    
}

struct StationSuche: Codable, Identifiable {
    var id = UUID()
    let stopLocationOrCoordLocation: [StopLocationOrCoordLocation]
    let technicalMessages: TechnicalMessages
    let serverVersion, dialectVersion, requestID: String
    
    enum CodingKeys: String, CodingKey {
        case stopLocationOrCoordLocation
        case technicalMessages = "TechnicalMessages"
        case serverVersion, dialectVersion
        case requestID = "requestId"
    }
}

// MARK: - StopLocationOrCoordLocation
struct StopLocationOrCoordLocation: Codable {
    let stopLocation: StopLocation
    
    enum CodingKeys: String, CodingKey {
        case stopLocation = "StopLocation"
    }
}

// MARK: - StopLocation
struct StopLocation: Identifiable, Codable {
    let locationNotes: LocationNotes
    let altID: [String]
    let timezoneOffset: Int
    let id, extID, name: String
    let lon, lat: Double
    let weight, products: Int
    
    enum CodingKeys: String, CodingKey {
        case locationNotes = "LocationNotes"
        case altID = "altId"
        case timezoneOffset, id
        case extID = "extId"
        case name, lon, lat, weight, products
    }
}

// MARK: - LocationNotes
struct LocationNotes: Codable {
    let locationNote: [LocationNote]
    
    enum CodingKeys: String, CodingKey {
        case locationNote = "LocationNote"
    }
}

// MARK: - LocationNote
struct LocationNote: Codable {
    let value, key, type, txtN: String
}

// MARK: - TechnicalMessages
struct TechnicalMessages: Codable {
    let technicalMessage: [TechnicalMessage]
    
    enum CodingKeys: String, CodingKey {
        case technicalMessage = "TechnicalMessage"
    }
}

// MARK: - TechnicalMessage
struct TechnicalMessage: Codable {
    let value, key: String
}
  • Related