arrays json multidimensional-array swift4 decodable

arrays - Swift 4 JSON Decodable con matriz multidimensional y multitipo



multidimensional-array swift4 (3)

{ "values":[ [1,1,7,"Azuan Child","Anak Azuan","12345","ACTIVE","Morning",7,12,"2017-11-09 19:45:00"], [28,1,0,"Azuan Child2","Amran","123456","ACTIVE","Evening",1,29,"2017-11-09 19:45:00"] ] }

Ok, este es mi formato json que recibí del servidor

Ahora mismo quiero descifrarlo en mi estructura, pero aún no tengo suerte.

struct ChildrenTable: Decodable { var values: [[String]]? }

Y mi método de llamada en URLSession se parece a esto

URLSession.shared.dataTask(with: request) { (data, response, err) in guard let data = data else { return } let dataAsString = String(data: data, encoding: .utf8) print(dataAsString) do { let children = try JSONDecoder().decode(ChildrenTable.self, from: data) print (children) } catch let jsonErr { print ("Error serializing json: ", jsonErr) } }.resume()

Y el error que obtuve son

Error serializing json: typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath: [Vito_Parent.ChildrenTable.(CodingKeys in _1B826CD7D9609504747BED0EC0B7D3B5).values, Foundation.(_JSONKey in _12768CA107A31EF2DCE034FD75B541C9)(stringValue: "Index 0", intValue: Optional(0)), Foundation.(_JSONKey in _12768CA107A31EF2DCE034FD75B541C9)(stringValue: "Index 0", intValue: Optional(0))], debugDescription: "Expected to decode String but found a number instead.", underlyingError: nil))

Sé que hay un int en la matriz y solo lanzo String para los valores var values: [[String]]? (la razón por la cual este error emergente), pero simplemente no puedo usar ninguna matriz multidimensional o tuplas en mis estructuras, ya que sigue el protocolo de Decodable.

Tampoco puedo convertir los datos en un diccionario, ya que arrojará el error "Se espera que decodifique el diccionario, pero se encontró una matriz en su lugar"

Alguna idea para resolver este problema? Intenté lanzar un tipo de cadena en los datos, pero todavía no tuve suerte ...

p / s: si todo el formato json está en tipo de cadena, no habría ningún problema, pero no tengo permiso para cambiar eso ya que lo llamo desde API.


Intenté utilizar tu solución en mi proyecto y funciona como un hechizo. A continuación modifiqué un poco para que pueda usarse para una sola cuerda, una sola matriz y una matriz multidimensional

struct TripModel: Decodable { var tx_result: Any var columns: [Any] var values: [[Any]] enum CodingKeys : String, CodingKey { case tx_result case columns case values } init(from decoder: Decoder) throws { var bigarr = [[Any]]() var arrColumn = [Any]() // get the dictionary let con = try! decoder.container(keyedBy: CodingKeys.self) let conResult = try! con.decode(String.self, forKey: CodingKeys.tx_result) var conColumns = try! con.nestedUnkeyedContainer(forKey: CodingKeys.columns) //print(String(describing: conColumns.count)) // get the "values" array of array var con2 = try! con.nestedUnkeyedContainer(forKey: CodingKeys.values) for _ in 0..<con2.count! { // get a nested array var con3 = try! con2.nestedUnkeyedContainer() // decode all the elements of the nested array var arr = [Any]() arr.append(try! con3.decode(Int.self)) arr.append(try! con3.decode(Int.self)) arr.append(try! con3.decode(Int.self)) arr.append(try! con3.decode(Int.self)) arr.append(try! con3.decode(String.self)) arr.append(try! con3.decode(String.self)) arr.append(try! con3.decode(String.self)) arr.append(try! con3.decode(Int.self)) arr.append(try! con3.decode(Int.self)) arr.append(try! con3.decode(Double.self)) arr.append(try! con3.decode(String.self)) bigarr.append(arr) } arrColumn.append(try! conColumns.decode(String.self)) arrColumn.append(try! conColumns.decode(String.self)) arrColumn.append(try! conColumns.decode(String.self)) arrColumn.append(try! conColumns.decode(String.self)) arrColumn.append(try! conColumns.decode(String.self)) arrColumn.append(try! conColumns.decode(String.self)) arrColumn.append(try! conColumns.decode(String.self)) arrColumn.append(try! conColumns.decode(String.self)) arrColumn.append(try! conColumns.decode(String.self)) arrColumn.append(try! conColumns.decode(String.self)) arrColumn.append(try! conColumns.decode(String.self)) // all done! finish initialization self.tx_result = conResult self.columns = arrColumn self.values = bigarr }

}


Como dijiste, tu matriz json es de tipo múltiple, pero estás tratando de descodificar todo en String . La conformidad por defecto de String to Decodable no permite eso. La única solución que viene a mi mente es introducir un nuevo tipo.

struct IntegerOrString: Decodable { var value: Any init(from decoder: Decoder) throws { if let int = try? Int(from: decoder) { value = int return } value = try String(from: decoder) } } struct ChildrenTable: Decodable { var values: [[IntegerOrString]]? }

Ejecutar en línea


Observe que los arreglos internos en el JSON tienen una secuencia de tipos de patrones, y sabemos cuál es esa secuencia. Los tipos en las matrices internas están en una secuencia de patrones: 3 Ints, 5 Strings, 2 Ints, y algo probablemente pensado como una Fecha. Claramente, en la mente de los diseñadores de JSON, cada uno de estos 11 elementos tiene un significado fijo y conocido.

Esto significa que podemos recoger los 11 elementos uno por uno, manualmente, mediante inmersión en contenedor y decodificando manualmente la expresión JSON completa.

Las matrices tienen tipos mixtos, y a Swift no le gusta eso, así que tendremos que expresarlos como una matriz de Cualquiera (o de AnyObject); pero podemos obtenerlos como ellos mismos , en lugar de tener que envolverlos en una estructura intermedia artificial.

Por cierto, si sabes cuál es el significado de cada elemento, en lugar de una matriz de Cualquiera, puedes decodificar la matriz interna en una estructura con 11 propiedades nombradas que expresan lo que significa cada elemento. Eso sería un resultado más limpio, pero no lo he usado porque no sé el significado de los 11 valores.

Aquí vamos:

struct S : Decodable { var values : [[Any]] enum CodingKeys : String, CodingKey { case values } init(from decoder: Decoder) throws { // get the dictionary let con = try! decoder.container(keyedBy: CodingKeys.self) // get the "values" array of array var con2 = try! con.nestedUnkeyedContainer(forKey: CodingKeys.values) var bigarr = [[Any]]() for _ in 0..<con2.count! { // get a nested array var con3 = try! con2.nestedUnkeyedContainer() // decode all the elements of the nested array var arr = [Any]() arr.append(try! con3.decode(Int.self)) arr.append(try! con3.decode(Int.self)) arr.append(try! con3.decode(Int.self)) arr.append(try! con3.decode(String.self)) arr.append(try! con3.decode(String.self)) arr.append(try! con3.decode(String.self)) arr.append(try! con3.decode(String.self)) arr.append(try! con3.decode(String.self)) arr.append(try! con3.decode(Int.self)) arr.append(try! con3.decode(Int.self)) arr.append(try! con3.decode(String.self)) bigarr.append(arr) } // all done! finish initialization self.values = bigarr } } let result = try! JSONDecoder().decode(S.self, from: jdata) print(result.values) // [[1, 1, 7, "Azuan Child", "Anak Azuan", "12345", "ACTIVE", // "Morning", 7, 12, "2017-11-09 19:45:00"], // [28, 1, 0, "Azuan Child2", "Amran", "123456", "ACTIVE", // "Evening", 1, 29, "2017-11-09 19:45:00"]]