jsonserialization - parse json swift 4
Serialización JSON automática y deserialización de objetos en Swift (7)
Como se indica en WWDC2017 @ 24:48 ( Swift 4 ), podremos usar el protocolo codificable. Ejemplo
public struct Person : Codable {
public let firstName:String
public let lastName:String
public let location:Location
}
Entonces
let payload: Data = try JSONEncoder().encode(person)
Deserializar
let anotherPerson = try JSONDecoder().decode(Person.self, from: payload)
Todas las propiedades deben cumplir con el protocolo codificable.
Una alternativa , también puede ser JSONCodable que es utilizada por el generador de códigos de Swagger .
Estoy buscando una forma de serializar y deserializar automáticamente instancias de clase en Swift. Supongamos que hemos definido la siguiente clase ...
class Person {
let firstName: String
let lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
}
... y instancia de Person
:
let person = Person(firstName: "John", lastName: "Doe")
La representación JSON de la person
sería la siguiente:
{
"firstName": "John",
"lastName": "Doe"
}
Ahora, aquí están mis preguntas:
- ¿Cómo puedo serializar la instancia de
person
y obtener el JSON anterior sin tener que agregar manualmente todas las propiedades de la clase a un diccionario que se convierte en JSON? - ¿Cómo puedo deserializar el JSON anterior y recuperar un objeto instanciado que está tipado estáticamente para que sea del tipo
Person
? De nuevo, no quiero mapear las propiedades de forma manual.
Así es como lo harías en C # usando Json.NET :
var person = new Person("John", "Doe");
string json = JsonConvert.SerializeObject(person);
// {"firstName":"John","lastName":"Doe"}
Person deserializedPerson = JsonConvert.DeserializeObject<Person>(json);
Con Swift 4, simplemente tiene que hacer que su clase se ajuste a los Codable
( Encodable
y Encodable
) para poder realizar serialización y deserialización JSON.
import Foundation
class Person: Codable {
let firstName: String
let lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
}
Uso n. ° 1 (codifica una instancia de Person
en una cadena JSON):
let person = Person(firstName: "John", lastName: "Doe")
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted // if necessary
let data = try! encoder.encode(person)
let jsonString = String(data: data, encoding: .utf8)!
print(jsonString)
/*
prints:
{
"firstName" : "John",
"lastName" : "Doe"
}
*/
Uso # 2 (decodifica una cadena JSON en una instancia de Person
):
let jsonString = """
{
"firstName" : "John",
"lastName" : "Doe"
}
"""
let jsonData = jsonString.data(using: .utf8)!
let decoder = JSONDecoder()
let person = try! decoder.decode(Person.self, from: jsonData)
dump(person)
/*
prints:
▿ __lldb_expr_609.Person #0
- firstName: "John"
- lastName: "Doe"
*/
Eche un vistazo a NSKeyValueCoding.h, específicamente setValuesForKeysWithDictionary
. Una vez que deserialice el json en un NSDictionary
, puede crear e inicializar su objeto con esa instancia de diccionario, sin necesidad de establecer valores manualmente en el objeto. Esto le dará una idea de cómo la deserialización podría funcionar con json, pero pronto descubrirá que necesita más control sobre el proceso de deserialización. Esta es la razón por la cual implemento una categoría en NSObject
que permite la inicialización de NSObject
completamente controlada con un diccionario durante la deserialización json, básicamente enriquece el objeto incluso más de lo que setValuesForKeysWithDictionary
puede hacer. También tengo un protocolo utilizado por el deserializador json, que permite deserializar el objeto para controlar ciertos aspectos, por ejemplo, si deserializa un NSArray de objetos, le preguntará a ese objeto cuál es el nombre de tipo de los objetos almacenados en el conjunto.
Hay una clase Foundation llamada NSJSONSerialization
que puede realizar conversiones desde y hacia JSON
.
El método para convertir de JSON a un objeto se ve así:
let jsonObject = NSJSONSerialization.JSONObjectWithData(data,
options: NSJSONReadingOptions.MutableContainers,
error: &error) as NSDictionary
Tenga en cuenta que el primer argumento para este método es la información JSON
, pero no como un objeto de cadena, sino como un objeto NSData
(que es la forma en que muchas veces obtendrá datos JSON de todos modos).
Lo más probable es que desee un método de fábrica para su clase que tome datos JSON como argumento, haga uso de este método y devuelva un objeto de inicialización de su clase.
Para invertir este proceso y crear datos JSON a partir de un objeto, querrá hacer uso de dataWithJSONObject
, en el que pasará un objeto que se puede convertir en JSON y tener un NSData?
devuelto. De nuevo, es probable que desee crear un método auxiliar que no requiera argumentos como método de instancia de su clase.
Hasta donde yo sé, la forma más fácil de manejar esto es crear una forma de mapear las propiedades de sus objetos en un diccionario y pasar ese diccionario para convertir su objeto en datos JSON. Luego, al convertir sus datos JSON en el objeto, espere que se devuelva un diccionario e invierta el proceso de asignación. Sin embargo, puede haber una manera más fácil.
Podría usar EVReflection para eso. Puede usar código como:
var person:Person = Person(json:jsonString)
o
var jsonString:String = person.toJsonString()
Consulte la página de GitHub para obtener un código de muestra más detallado. Solo tiene que hacer de EVObject la clase base de sus objetos de datos. No se necesita mapeo (siempre que las claves json sean las mismas que los nombres de las propiedades)
Actualización: Swift 4 tiene soporte para Codable, lo que lo hace casi tan fácil como EVReflection pero con un mejor rendimiento. Si desea utilizar un contratista fácil como el anterior, puede usar esta extensión: Stuff/Codable
Primero, crea un objeto Swift como este
struct Person {
var firstName: String?;
var lastName: String?;
init() {
}
}
Después de eso, serialice los datos JSON que ha obtenido, utilizando la NSJSONSerialization incorporada y analice los valores en el objeto Person .
var person = Person();
var error: NSError?;
var response: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(), error: &error);
if let personDictionary = response as? NSDictionary {
person.firstName = personDictionary["firstName"] as? String;
person.lastName = personDictionary["lastName"] as? String;
}
ACTUALIZAR:
También por favor eche un vistazo a esas bibliotecas
Puede lograr esto utilizando la biblioteca ObjectMapper . Te dará más control sobre nombres de variables y los valores y el JSON preparado. Después de agregar esta biblioteca, amplíe la clase Mappable
y defina la función de mapping(map: Map)
.
Por ejemplo
class User: Mappable {
var id: Int?
var name: String?
required init?(_ map: Map) {
}
// Mapping code
func mapping(map: Map) {
name <- map["name"]
id <- map["id"]
}
}
Úselo como abajo
let user = Mapper<User>().map(JSONString)