es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Analizando Json desde una Web API

Hola, mi nombre es Nico,

Soy un principiante en programación de aplicaciones/SwiftUI.
Estoy intentando analizar datos json de una API web pero por alguna razón no puedo analizar los datos correctamente. Supongo que mi estructura json no es correcta, pero no puedo encontrar el problema.

El Json que obtengo de la API web se ve algo así:

{
“pois”: [
{
“id”: “2635094451”,
“lat”: “52.410150”,
“lat_s”: “52.4”,
“lng”: “10.776630”,
“lng_s”: “10.8”,
“street”: “Röntgenstraße”,
“content”: “8137285512”,
“backend”: “0-239283152”,
“type”: “1”,
“vmax”: “50”,
“counter”: “0”,
“create_date”: “2021-11-18 13:21:50”,
“confirm_date”: “2021-11-18 13:21:43”,
“gps_status”: “-“,
“info”: ” {\”qltyCountryRoad\”:1,\”confirmed\”:\”0\”,\”gesperrt\”:\”0\”,\”precheck\”:\”[Q1|21|0]\”}”,
“polyline”: “”
}
],
“grid”: []
}

Mi estructura se ve así:

struct APIResponse: Codable {
let pois: [InputDataPois]
let grid: [InputDataGrid]

private enum CodingKeys: String, CodingKey {
    case pois
    case grid
}

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.pois = try container.decode(APIResponse.self, forKey: .pois).pois
    self.grid = try container.decode(APIResponse.self, forKey: .grid).grid
}

}

struct InputDataPois: Codable, Identifiable {
let id:String
let lat:String
let lat_s:String
let lng:String
let lng_s:String
let street:String
let content:String
let backend:String
let type:String
let vmax:String
let counter:String
let create_date:String
let confirm_date:String
let gps_status:String
let info:String
let polyline:String
}

extension InputDataPois {
private enum CodingKeys: String, CodingKey {
case id
case lat
case lat_s
case lng
case lng_s
case street
case content
case backend
case type
case vmax
case counter
case create_date
case confirm_date
case gps_status
case info
case polyline
}

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)

    self.id = try container.decode(String.self, forKey: .id)
    self.lat = try container.decode(String.self, forKey: .lat)
    self.lat_s = try container.decode(String.self, forKey: .lat_s)
    self.lng = try container.decode(String.self, forKey: .lng)
    self.lng_s = try container.decode(String.self, forKey: .lng_s)
    self.street = try container.decode(String.self, forKey: .street)
    self.content = try container.decode(String.self, forKey: .content)
    self.backend = try container.decode(String.self, forKey: .backend)
    self.type = try container.decode(String.self, forKey: .type)
    self.vmax = try container.decode(String.self, forKey: .vmax)
    self.counter = try container.decode(String.self, forKey: .counter)
    self.create_date = try container.decode(String.self, forKey: .create_date)
    self.confirm_date = try container.decode(String.self, forKey: .confirm_date)
    self.gps_status = try container.decode(String.self, forKey: .gps_status)
    self.info = try container.decode(String.self, forKey: .info)
    self.polyline = try container.decode(String.self, forKey: .polyline)
}

}

struct InputDataGrid: Codable {

}

Y mi paquete se ve así:

extension Bundle {
func decode(_ file: String) -> [InputDataPois] {
// 1. Localiza el archivo Json
guard let url = URL(string: file) else {
fatalError(“Error al localizar (file) en el paquete”)
}

    // 2. Crea una propiedad para los datos
    guard let data = try? Data(contentsOf: url) else {
        fatalError("Error al cargar \(file) desde el paquete.")
    }

    // 3. Crea una propiedad para los datos
    let str = String(decoding: data, as: UTF8.self)
    print("\(str)")

    guard let loaded = try? JSONDecoder().decode(APIResponse.self, from: data).pois else {
        fatalError("Error al decodificar \(file) del paquete.")
    }
    // 4. Retorna los datos listos para usar
    return loaded
}

}

Y en mi vista estoy usando:

let speed: [InputDataPois] = Bundle.main.decode(“https://cdn2.atudo.net/api/1.0/vl.php?type=0,1,2,3,4,5,6&box=52.36176390234046,10.588760375976562,52.466468685912744,11.159706115722656”)

El error que estoy obteniendo se parece a esto:

(ErrorMessage)[https://i.stack.imgur.com/HoaoC.png]
(ConsoleError)[https://i.stack.imgur.com/aA2tW.png]

Gracias de antemano por su ayuda.

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Solución:

    Al analizar los mensajes de error, parece que el problema se encuentra en la decodificación de los datos JSON. En concreto, el mensaje de error “Se esperaba un Array pero se encontró un diccionario.” sugiere que los datos JSON pueden no estar estructurados como se esperaba.

    Según el ejemplo de datos JSON proporcionado, parece que el campo “pois” es una matriz de objetos, mientras que el campo “grid” es una matriz vacía. Sin embargo, en la estructura APIResponse, ambos campos están definidos como matrices de diferentes tipos de datos. Parece que hay una discrepancia entre los datos y la estructura de datos definida.

    Para solucionar este problema, podrías intentar actualizar la estructura APIResponse para que coincida con la estructura de los datos JSON. Aquí hay una versión actualizada de la estructura APIResponse:

    struct APIResponse: Codable {
        let pois: [InputDataPois]
        let grid: [Any] // actualiza grid para que sea un array de Any
    
        private enum CodingKeys: String, CodingKey {
            case pois
            case grid
        }
    
        init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            self.pois = try container.decode([InputDataPois].self, forKey: .pois) // decodifica pois directamente a un array de InputDataPois
            self.grid = try container.decodeIfPresent([Any].self, forKey: .grid) ?? [] // decodifica grid como un array de Any
        }
    }
    

    En esta versión actualizada, el campo pois se decodifica directamente como un array de InputDataPois, mientras que el campo grid se define como un array de Any. Esto debería permitir que los datos JSON se analicen correctamente, sin encontrar errores de decodificación.

    Una vez que se actualiza la estructura APIResponse, deberías poder utilizarla como antes para decodificar los datos JSON en la extensión Bundle. Aquí está la versión actualizada de la extensión Bundle:

    extension Bundle {
        func decode(_ url: String) -> [InputDataPois] {
            // 1. Convierte la cadena de URL a URL
            guard let url = URL(string: url) else {
                fatalError("No se pudo localizar la URL: \(url)")
            }
    
            // 2. Carga los datos desde la URL
            guard let data = try? Data(contentsOf: url) else {
                fatalError("No se pudieron cargar los datos desde la URL: \(url)")
            }
    
            // 3. Decodifica los datos utilizando la estructura APIResponse actualizada
            let decoder = JSONDecoder()
            let response = try? decoder.decode(APIResponse.self, from: data)
    
            // 4. Devuelve la matriz pois de la respuesta decodificada
            return response?.pois ?? []
        }
    }
    

    En esta versión actualizada, la cadena de URL se convierte en un objeto URL antes de cargar los datos. Se crea el decodificador JSON con la estructura APIResponse actualizada y se extrae y devuelve el array pois decodificado.

    Con estas actualizaciones, deberías poder decodificar correctamente los datos JSON y utilizarlos en tu aplicación. Hazme saber si tienes alguna pregunta o problema adicional.

Comments are closed.