serialize parse data json swift swift4 codable

json - parse - Usar codificable con clave que a veces es una Int y otras veces una Cadena



swift 4 data to json (2)

Esta es una posible solución con MetadataType , lo bueno es que puede ser una solución general no solo para GeneralProduct , sino para todas las struct tienen la misma ambigüedad:

struct GeneralProduct: Codable { var price:Double? var id:MetadataType? var name:String? private enum CodingKeys: String, CodingKey { case price = "p" case id = "i" case name = "n" } init(price:Double? = nil, id: MetadataType? = nil, name: String? = nil) { self.price = price self.id = id self.name = name } } enum MetadataType: Codable { case int(Int) case string(String) init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() do { self = try .int(container.decode(Int.self)) } catch DecodingError.typeMismatch { do { self = try .string(container.decode(String.self)) } catch DecodingError.typeMismatch { throw DecodingError.typeMismatch(MetadataType.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Encoded payload not of an expected type")) } } } func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch self { case .int(let int): try container.encode(int) case .string(let string): try container.encode(string) } } }

esta es la prueba:

let decoder = JSONDecoder() var json = "{/"p/":2.19,/"i/":0,/"n/":/"Black Shirt/"}" var product = try! decoder.decode(GeneralProduct.self, from: json.data(using: .utf8)!) if let id = product.id { print(id) // 0 } json = "{/"p/":2.19,/"i/":/"hello world/",/"n/":/"Black Shirt/"}" product = try! decoder.decode(GeneralProduct.self, from: json.data(using: .utf8)!) if let id = product.id { print(id) // hello world }

Tengo una API que a veces devolverá una clave específica (en este caso, id ) en el JSON como Int y otras veces devolverá la misma clave como String. ¿Cómo uso codificable para analizar ese JSON?

struct GeneralProduct: Codable { var price:Double! var id:String? var name:String! private enum CodingKeys: String, CodingKey { case price = "p" case id = "i" case name = "n" } init(price:Double? = nil, id: String? = nil, name: String? = nil) { self.price = price self.id = id self.name = name } }

Sigo recibiendo este mensaje de error: se Expected to decode String but found a number instead . La razón por la que devuelve un número es porque el campo de identificación está vacío y cuando el campo de identificación está vacío, el valor predeterminado es devolver 0 como una ID que identifica de forma codificable como un número. Básicamente, puedo ignorar la clave de identificación, pero codificar no me da la opción de ignorarla, que yo sepa. ¿Cuál sería la mejor manera de manejar esto?

Aquí está el JSON. Es super simple

Trabajando

{ "p":2.12, "i":"3k3mkfnk3", "n":"Blue Shirt" }

Error: debido a que no hay una identificación en el sistema, devuelve 0 como valor predeterminado, que el codificador obviamente ve como un número opuesto a la cadena.

{ "p":2.19, "i":0, "n":"Black Shirt" }


struct GeneralProduct: Codable { var price: Double? var id: String? var name: String? private enum CodingKeys: String, CodingKey { case price = "p", id = "i", name = "n" } init(price: Double? = nil, id: String? = nil, name: String? = nil) { self.price = price self.id = id self.name = name } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) price = try container.decode(Double.self, forKey: .price) name = try container.decode(String.self, forKey: .name) if let value = try? container.decode(Int.self, forKey: .id) { id = String(value) } else { id = try container.decode(String.self, forKey: .id) } } }

let json1 = """ { "p":2.12, "i":"3k3mkfnk3", "n":"Blue Shirt" } """ let json2 = """ { "p":2.12, "i":0, "n":"Blue Shirt" } """

do { let product = try JSONDecoder().decode(GeneralProduct.self, from: Data(json2.utf8)) print(product.price ?? "") print(product.id ?? "") print(product.name ?? "") } catch { print(error) }

editar / actualizar :

También puede simplemente asignar nil a su id cuando su API devuelve 0 :

if let _ = try? container.decode(Int.self, forKey: .id) { id = nil } else { id = try container.decode(String.self, forKey: .id) }