Swift Language
Lectura y escritura JSON
Buscar..
Sintaxis
- NSJSONSerialization.JSONObjectWithData (jsonData, opciones: NSJSONReadingOptions) // Devuelve un objeto de jsonData. Este método arroja en el fracaso.
- NSJSONSerialization.dataWithJSONObject (jsonObject, opciones: NSJSONWritingOptions) // Devuelve NSData de un objeto JSON. Pase en NSJSONWritingOptions.PrettyPrinted en opciones para una salida que sea más legible.
Serialización, codificación y decodificación JSON con Apple Foundation y Swift Standard Library
La clase JSONSerialization está integrada en el marco de la Fundación de Apple.
Leer json
La función JSONObjectWithData
toma NSData
y devuelve AnyObject
. Se puede usar as?
para convertir el resultado a su tipo esperado.
do {
guard let jsonData = "[\"Hello\", \"JSON\"]".dataUsingEncoding(NSUTF8StringEncoding) else {
fatalError("couldn't encode string as UTF-8")
}
// Convert JSON from NSData to AnyObject
let jsonObject = try NSJSONSerialization.JSONObjectWithData(jsonData, options: [])
// Try to convert AnyObject to array of strings
if let stringArray = jsonObject as? [String] {
print("Got array of strings: \(stringArray.joinWithSeparator(", "))")
}
} catch {
print("error reading JSON: \(error)")
}
Puede pasar las options: .AllowFragments
que se options: .AllowFragments
lugar de las options: []
para permitir la lectura de JSON cuando el objeto de nivel superior no es una matriz o diccionario.
Escribir json
Al llamar a dataWithJSONObject
convierte un objeto compatible con JSON (matrices anidadas o diccionarios con cadenas, números y NSNull
) en NSData
bruto codificados como UTF-8.
do {
// Convert object to JSON as NSData
let jsonData = try NSJSONSerialization.dataWithJSONObject(jsonObject, options: [])
print("JSON data: \(jsonData)")
// Convert NSData to String
let jsonString = String(data: jsonData, encoding: NSUTF8StringEncoding)!
print("JSON string: \(jsonString)")
} catch {
print("error writing JSON: \(error)")
}
Puede pasar las options: .PrettyPrinted
lugar de las options: []
para impresión bonita.
Mismo comportamiento en Swift 3 pero con una sintaxis diferente.
do {
guard let jsonData = "[\"Hello\", \"JSON\"]".data(using: String.Encoding.utf8) else {
fatalError("couldn't encode string as UTF-8")
}
// Convert JSON from NSData to AnyObject
let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: [])
// Try to convert AnyObject to array of strings
if let stringArray = jsonObject as? [String] {
print("Got array of strings: \(stringArray.joined(separator: ", "))")
}
} catch {
print("error reading JSON: \(error)")
}
do {
// Convert object to JSON as NSData
let jsonData = try JSONSerialization.data(withJSONObject: jsonObject, options: [])
print("JSON data: \(jsonData)")
// Convert NSData to String
let jsonString = String(data: jsonData, encoding: .utf8)!
print("JSON string: \(jsonString)")
} catch {
print("error writing JSON: \(error)")
}
Nota: Lo siguiente está disponible actualmente solo en Swift 4.0 y versiones posteriores.
A partir de Swift 4.0, la biblioteca estándar Swift incluye los protocolos Encodable
y Decodable
para definir un enfoque estandarizado para la codificación de datos y la decodificación. La adopción de estos protocolos permitirá que las implementaciones de los protocolos de Encoder
y Decoder
tomen sus datos y los codifiquen o descodifiquen hacia y desde una representación externa como JSON. La conformidad con la Codable
protocolo combina tanto la Encodable
y Decodable
protocolos. Este es ahora el medio recomendado para manejar JSON en su programa.
Codificar y decodificar automáticamente
La forma más fácil de hacer un tipo codificable es declarar sus propiedades como tipos que ya son Codable
. Estos tipos incluyen tipos de biblioteca estándar como String
, Int
y Double
; y tipos de fundaciones, como Date
, Data
y URL
. Si las propiedades de un tipo son codificables, el tipo en sí se ajustará automáticamente a Codable
simplemente declarando la conformidad.
Considere el siguiente ejemplo, en el que la estructura del Book
se ajusta a Codable
.
struct Book: Codable {
let title: String
let authors: [String]
let publicationDate: Date
}
Tenga en cuenta que las colecciones estándar como
Array
yDictionary
ajustan aCodable
si contienen tipos codificables.
Al adoptar Codable
, la estructura del Book
ahora se puede codificar y descodificar desde JSON utilizando las clases JSONEncoder
y JSONDecoder
Apple Foundation, aunque Book
no contiene ningún código para manejar específicamente JSON. Los codificadores y decodificadores personalizados también pueden escribirse, de conformidad con los protocolos Encoder
y Decoder
, respectivamente.
Codificar a datos JSON
// Create an instance of Book called book
let encoder = JSONEncoder()
let data = try! encoder.encode(book) // Do not use try! in production code
print(data)
Configure
encoder.outputFormatting = .prettyPrinted
para una lectura más fácil. ## Decodificar a partir de datos JSON
Decodificar a partir de datos JSON
// Retrieve JSON string from some source
let jsonData = jsonString.data(encoding: .utf8)!
let decoder = JSONDecoder()
let book = try! decoder.decode(Book.self, for: jsonData) // Do not use try! in production code
print(book)
En el ejemplo anterior,
Book.self
informa al decodificador del tipo al que se debe decodificar el JSON.
Codificación o decodificación exclusiva
A veces es posible que no necesite que los datos sean codificables y decodificables, como cuando solo necesita leer datos JSON de una API, o si su programa solo envía datos JSON a una API.
Si solo desea escribir datos JSON, ajuste su tipo a Encodable
.
struct Book: Encodable {
let title: String
let authors: [String]
let publicationDate: Date
}
Si solo desea leer datos JSON, ajuste su tipo a Decodable
.
struct Book: Decodable {
let title: String
let authors: [String]
let publicationDate: Date
}
Usando Nombres Clave Personalizados
Las API utilizan con frecuencia convenciones de nomenclatura distintas de la caja de camello estándar de Swift, como la caja de serpiente. Esto puede convertirse en un problema cuando se trata de decodificar JSON, ya que, de forma predeterminada, las claves JSON deben alinearse exactamente con los nombres de propiedad de su tipo. Para manejar estos escenarios, puede crear claves personalizadas para su tipo usando el protocolo CodingKey
.
struct Book: Codable {
// ...
enum CodingKeys: String, CodingKey {
case title
case authors
case publicationDate = "publication_date"
}
}
CodingKeys
se generan automáticamente para los tipos que adoptan el protocolo Codable
, pero al crear nuestra propia implementación en el ejemplo anterior, permitimos que nuestro decodificador Codable
coincidir la fecha de publicationDate
caso de camello local con la fecha de publication_date
caso de serpiente como lo entrega la API.
SwiftyJSON
SwiftyJSON es un marco Swift creado para eliminar la necesidad de un encadenamiento opcional en la serialización JSON normal.
Puede descargarlo aquí: https://github.com/SwiftyJSON/SwiftyJSON
Sin SwiftyJSON, su código se vería así para encontrar el nombre del primer libro en un objeto JSON:
if let jsonObject = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments) as? [[String: AnyObject]],
let bookName = (jsonObject[0]["book"] as? [String: AnyObject])?["name"] as? String {
//We can now use the book name
}
En SwiftyJSON, esto se simplifica enormemente:
let json = JSON(data: data)
if let bookName = json[0]["book"]["name"].string {
//We can now use the book name
}
Elimina la necesidad de verificar todos los campos, ya que devolverá cero si alguno de ellos no es válido.
Para usar SwiftyJSON, descargue la versión correcta desde el repositorio de Git. Hay una rama para Swift 3. Simplemente arrastre el "SwiftyJSON.swift" a su proyecto e importe a su clase:
import SwiftyJSON
Puedes crear tu objeto JSON usando los siguientes dos inicializadores:
let jsonObject = JSON(data: dataObject)
o
let jsonObject = JSON(jsonObject) //This could be a string in a JSON format for example
Para acceder a sus datos, utilice subíndices:
let firstObjectInAnArray = jsonObject[0]
let nameOfFirstObject = jsonObject[0]["name"]
Luego, puede analizar su valor a un determinado tipo de datos, que devolverá un valor opcional:
let nameOfFirstObject = jsonObject[0]["name"].string //This will return the name as a string
let nameOfFirstObject = jsonObject[0]["name"].double //This will return null
También puede compilar sus rutas en una matriz rápida:
let convolutedPath = jsonObject[0]["name"][2]["lastName"]["firstLetter"].string
Es lo mismo que:
let convolutedPath = jsonObject[0, "name", 2, "lastName", "firstLetter"].string
SwiftyJSON también tiene funcionalidad para imprimir sus propios errores:
if let name = json[1337].string {
//You can use the value - it is valid
} else {
print(json[1337].error) // "Array[1337] is out of bounds" - You cant use the value
}
Si necesita escribir en su objeto JSON, puede usar subíndices de nuevo:
var originalJSON:JSON = ["name": "Jack", "age": 18]
originalJSON["age"] = 25 //This changes the age to 25
originalJSON["surname"] = "Smith" //This creates a new field called "surname" and adds the value to it
Si necesita la cadena original para el JSON, por ejemplo, si necesita escribirla en un archivo, puede obtener el valor en bruto:
if let string = json.rawString() { //This is a String object
//Write the string to a file if you like
}
if let data = json.rawData() { //This is an NSData object
//Send the data to your server if you like
}
Freddy
Freddy es una biblioteca de análisis JSON mantenida por Big Nerd Ranch . Tiene tres ventajas principales:
Escriba seguridad: lo ayuda a trabajar con el envío y la recepción de JSON de forma que se eviten bloqueos en el tiempo de ejecución.
Idiomatic: aprovecha los genéricos, las enumeraciones y las funciones funcionales de Swift, sin documentación complicada ni operadores mágicos personalizados.
Manejo de errores: proporciona información de errores informativa para errores JSON comunes.
Ejemplo de datos JSON
Definamos algunos ejemplos de datos JSON para usar con estos ejemplos.
{ "success": true, "people": [ { "name": "Matt Mathias", "age": 32, "spouse": true }, { "name": "Sergeant Pepper", "age": 25, "spouse": false } ], "jobs": [ "teacher", "judge" ], "states": { "Georgia": [ 30301, 30302, 30303 ], "Wisconsin": [ 53000, 53001 ] } }
let jsonString = "{\"success\": true, \"people\": [{\"name\": \"Matt Mathias\",\"age\": 32,\"spouse\": true},{\"name\": \"Sergeant Pepper\",\"age\": 25,\"spouse\": false}],\"jobs\": [\"teacher\",\"judge\"],\"states\": {\"Georgia\": [30301,30302,30303],\"Wisconsin\": [53000,53001]}}" let jsonData = jsonString.dataUsingEncoding(NSUTF8StringEncoding)!
Deserialización de datos sin procesar
Para deserializar los datos, inicializamos un objeto JSON
luego accedemos a una clave particular.
do { let json = try JSON(data: jsonData) let success = try json.bool("success") } catch { // do something with the error }
Nosotros try
aquí porque el acceso a la json
para la tecla de "success"
podría fallar - que podría no existe, o el valor para no ser un valor lógico.
También podemos especificar una ruta para acceder a los elementos anidados en la estructura JSON. La ruta es una lista de claves e índices separados por comas que describen la ruta a un valor de interés.
do { let json = try JSON(data: jsonData) let georgiaZipCodes = try json.array("states", "Georgia") let firstPersonName = try json.string("people", 0, "name") } catch { // do something with the error }
Deserializando modelos directamente
JSON se puede analizar directamente a una clase modelo que implementa el protocolo JSONDecodable
.
public struct Person { public let name: String public let age: Int public let spouse: Bool } extension Person: JSONDecodable { public init(json: JSON) throws { name = try json.string("name") age = try json.int("age") spouse = try json.bool("spouse") } } do { let json = try JSON(data: jsonData) let people = try json.arrayOf("people", type: Person.self) } catch { // do something with the error }
Serialización de datos en bruto
Cualquier valor JSON
puede ser serializado directamente a NSData
.
let success = JSON.Bool(false) let data: NSData = try success.serialize()
Serialización de modelos directamente
Cualquier clase de modelo que implemente el protocolo JSONEncodable
se puede serializar directamente a NSData
.
extension Person: JSONEncodable { public func toJSON() -> JSON { return .Dictionary([ "name": .String(name), "age": .Int(age), "spouse": .Bool(spouse) ]) } } let newPerson = Person(name: "Glenn", age: 23, spouse: true) let data: NSData = try newPerson.toJSON().serialize()
Flecha
Arrow es una elegante biblioteca de análisis JSON en Swift.
Permite analizar JSON y asignarlo a clases de modelos personalizados con la ayuda de un operador <--
:
identifier <-- json["id"]
name <-- json["name"]
stats <-- json["stats"]
Ejemplo:
Modelo swift
struct Profile {
var identifier = 0
var name = ""
var link: NSURL?
var weekday: WeekDay = .Monday
var stats = Stats()
var phoneNumbers = [PhoneNumber]()
}
Archivo JSON
{
"id": 15678,
"name": "John Doe",
"link": "https://apple.com/steve",
"weekdayInt" : 3,
"stats": {
"numberOfFriends": 163,
"numberOfFans": 10987
},
"phoneNumbers": [{
"label": "house",
"number": "9809876545"
}, {
"label": "cell",
"number": "0908070656"
}, {
"label": "work",
"number": "0916570656"
}]
}
Cartografía
extension Profile: ArrowParsable {
mutating func deserialize(json: JSON) {
identifier <-- json["id"]
link <-- json["link"]
name <-- json["name"]
weekday <-- json["weekdayInt"]
stats <- json["stats"]
phoneNumbers <-- json["phoneNumbers"]
}
}
Uso
let profile = Profile()
profile.deserialize(json)
Instalación:
Cartago
github "s4cha/Arrow"
CocoaPods
pod 'Arrow'
use_frameworks!
A mano
Simplemente copie y pegue Arrow.swift en su proyecto Xcode
https://github.com/s4cha/Arrow
Como un marco
Descargue Arrow desde el repositorio de GitHub y genere el objetivo de Framework en el proyecto de ejemplo. Entonces enlace contra este marco.
JSON simple de análisis en objetos personalizados
Incluso si las bibliotecas de terceros son buenas, los protocolos proporcionan una forma sencilla de analizar JSON. Puedes imaginar que tienes un objeto Todo
como
struct Todo {
let comment: String
}
Cada vez que reciba el JSON, puede manejar el NSData
simple como se muestra en el otro ejemplo utilizando el objeto NSJSONSerialization
.
Después de eso, usando un protocolo simple JSONDecodable
typealias JSONDictionary = [String:AnyObject]
protocol JSONDecodable {
associatedtype Element
static func from(json json: JSONDictionary) -> Element?
}
Y hacer que tu estructura de Todo
ajuste a JSONDecodable
hace el truco
extension Todo: JSONDecodable {
static func from(json json: JSONDictionary) -> Todo? {
guard let comment = json["comment"] as? String else { return nil }
return Todo(comment: comment)
}
}
Puedes probarlo con este código json:
{
"todos": [
{
"comment" : "The todo comment"
}
]
}
Cuando lo obtuvo de la API, puede serializarlo como los ejemplos anteriores que se muestran en una instancia de AnyObject
. Después de eso, puedes verificar si la instancia es una instancia de JSONDictionary
guard let jsonDictionary = dictionary as? JSONDictionary else { return }
La otra cosa a verificar, específica para este caso porque tiene una matriz de Todo
en el JSON, es el diccionario todos
guard let todosDictionary = jsonDictionary["todos"] as? [JSONDictionary] else { return }
Ahora que tiene la matriz de diccionarios, puede convertir cada uno de ellos en un objeto Todo
utilizando flatMap
(eliminará automáticamente los valores nil
de la matriz)
let todos: [Todo] = todosDictionary.flatMap { Todo.from(json: $0) }
JSON analizando Swift 3
Aquí está el archivo JSON que animals.json
llamado animals.json
{
"Sea Animals": [
{
"name": "Fish",
"question": "How many species of fish are there?" },
{
"name": "Sharks",
"question": "How long do sharks live?"
},
{
"name": "Squid",
"question": "Do squids have brains?"
},
{
"name": "Octopus",
"question": "How big do octopus get?"
},
{
"name": "Star Fish",
"question": "How long do star fish live?"
}
],
"mammals": [
{
"name": "Dog",
"question": "How long do dogs live?"
},
{
"name": "Elephant",
"question": "How much do baby elephants weigh?"
},
{
"name": "Cats",
"question": "Do cats really have 9 lives?"
},
{
"name": "Tigers",
"question": "Where do tigers live?"
},
{
"name": "Pandas",
"question": "WHat do pandas eat?"
} ] }
Importa tu archivo JSON a tu proyecto
Puede realizar esta sencilla función para imprimir su archivo JSON
func jsonParsingMethod() {
//get the file
let filePath = Bundle.main.path(forResource: "animals", ofType: "json")
let content = try! String(contentsOfFile: filePath!)
let data: Data = content.data(using: String.Encoding.utf8)!
let json: NSDictionary = try! JSONSerialization.jsonObject(with: data as Data, options:.mutableContainers) as! NSDictionary
//Call which part of the file you'd like to pare
if let results = json["mammals"] as? [[String: AnyObject]] {
for res in results {
//this will print out the names of the mammals from out file.
if let rates = res["name"] as? String {
print(rates)
}
}
}
}
Si desea colocarlo en una vista de tabla, primero crearía un diccionario con un NSObject.
Cree un nuevo archivo swift llamado ParsingObject
y cree sus variables de cadena.
Asegúrese de que el nombre de la variable sea el mismo que el archivo JSON
. Por ejemplo, en nuestro proyecto tenemos name
y question
por lo tanto, en nuestro nuevo archivo swift, usaremos
var name: String?
var question: String?
Inicialice el objeto NSO que hicimos de nuevo en nuestro ViewController.swift var array = ParsingObject Luego, realizaríamos el mismo método que teníamos antes con una modificación menor.
func jsonParsingMethod() {
//get the file
let filePath = Bundle.main.path(forResource: "animals", ofType: "json")
let content = try! String(contentsOfFile: filePath!)
let data: Data = content.data(using: String.Encoding.utf8)!
let json: NSDictionary = try! JSONSerialization.jsonObject(with: data as Data, options:.mutableContainers) as! NSDictionary
//This time let's get Sea Animals
let results = json["Sea Animals"] as? [[String: AnyObject]]
//Get all the stuff using a for-loop
for i in 0 ..< results!.count {
//get the value
let dict = results?[i]
let resultsArray = ParsingObject()
//append the value to our NSObject file
resultsArray.setValuesForKeys(dict!)
array.append(resultsArray)
}
}
Luego lo mostramos en nuestra vista de tabla haciendo esto,
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
//This is where our values are stored
let object = array[indexPath.row]
cell.textLabel?.text = object.name
cell.detailTextLabel?.text = object.question
return cell
}