jsonserialization - Parse json en Swift, tipo AnyObject
swift 4 json to object (3)
Estoy tratando de analizar un json, pero tengo algunas dificultades con los tipos de datos y, notablemente, el tipo AnyObject + downcasting.
Consideremos el siguiente json (es un extracto de un json completo).
{ "weather":
[
{
"id":804,
"main":"Clouds",
"description":"overcast clouds",
"icon":"04d"
}
],
}
Para mí, el json se puede describir de la siguiente manera:
- json: Dictionary of type [String: AnyObject] (or NSDictionary, so = [NSObject, AnyObject] in Xcode 6 b3)
- "weather": Array of type [AnyObject] (or NSArray)
- Dictionary of type [String: AnyObject] (or NSDictionary, so = [NSObject, AnyObject] in Xcode 6 b3)
¡Mi json es del tipo AnyObject! (Utilizo JSONObjectWithData
para obtener el JSON de una URL).
Entonces quiero acceder al Diccionario meteorológico. Aquí está el código que escribí.
var localError: NSError?
var json: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &localError)
if let dict = json as? [String: AnyObject] {
if let weatherDictionary = dict["weather"] as? [AnyObject] {
// Do stuff with the weatherDictionary
}
}
Aquí está el error que obtuve
Playground execution failed: error: <EXPR>:28:56: error: ''[AnyObject]'' is not a subtype of ''(String, AnyObject)''
if let weatherDictionary = dict["weather"] as? [AnyObject] {
No entiendo por qué dict ["clima"] se compara con un subtipo de (String, AnyObject) y no AnyObject.
Decidí que mi diccionario era [String: AnyObject], así que accedí a un valor con la tecla String, debería tener AnyObject, ¿no?
Si uso NSDictionary en lugar de [String: AnyObject], funciona.
Si uso NSArray en lugar de [AnyObject], funciona.
- The Xcode 6 beta 3 release notes tell that "NSDictionary* is now imported from Objective-C APIs as [NSObject : AnyObject].".
- And the Swift book: "When you bridge from an NSArray object to a Swift array, the resulting array is of type [AnyObject]."
EDITAR
Me olvidé de obligar a desenvolver el dict ["clima"] !.
if let dict = json as? [String: AnyObject] {
println(dict)
if let weatherDictionary = dict["weather"]! as? [AnyObject] {
println("/nWeather dictionary:/n/n/(weatherDictionary)")
if let descriptionString = weatherDictionary[0]["description"]! as? String {
println("/nDescription of the weather is: /(descriptionString)")
}
}
}
Tenga en cuenta que deberíamos verificar dos veces la existencia del primer Opcional.
if let dict = json as? [String: AnyObject] {
for key in ["weather", "traffic"] {
if let dictValue = dict[key] {
if let subArray = dictValue as? [AnyObject] {
println(subArray[0])
}
} else {
println("Key ''/(key)'' not found")
}
}
}
Esto funciona bien para mí en el patio de recreo y en la terminal usando env xcrun swift
ACTUALIZADO PARA SWIFT 4 Y CODABLE
Aquí hay un ejemplo de Swift 4 que usa el protocolo codificable.
var jsonStr = "{/"weather/":[{/"id/":804,/"main/":/"Clouds/",/"description/":/"overcast clouds/",/"icon/":/"04d/"}],}"
struct Weather: Codable {
let id: Int
let main: String
let description: String
let icon: String
}
struct Result: Codable {
let weather: [Weather]
}
do {
let weather = try JSONDecoder().decode(Result.self, from: jsonStr.data(using: .utf8)!)
print(weather)
}
catch {
print(error)
}
ACTUALIZADO PARA SWIFT 3.0
He actualizado el código para Swift 3 y también se muestra cómo ajustar el JSON analizado en objetos. ¡Gracias por todos los votos!
import Foundation
struct Weather {
let id: Int
let main: String
let description: String
let icon: String
}
extension Weather {
init?(json: [String: Any]) {
guard
let id = json["id"] as? Int,
let main = json["main"] as? String,
let description = json["description"] as? String,
let icon = json["icon"] as? String
else { return nil }
self.id = id
self.main = main
self.description = description
self.icon = icon
}
}
var jsonStr = "{/"weather/":[{/"id/":804,/"main/":/"Clouds/",/"description/":/"overcast clouds/",/"icon/":/"04d/"}],}"
enum JSONParseError: Error {
case notADictionary
case missingWeatherObjects
}
var data = jsonStr.data(using: String.Encoding.ascii, allowLossyConversion: false)
do {
var json = try JSONSerialization.jsonObject(with: data!, options: [])
guard let dict = json as? [String: Any] else { throw JSONParseError.notADictionary }
guard let weatherJSON = dict["weather"] as? [[String: Any]] else { throw JSONParseError.missingWeatherObjects }
let weather = weatherJSON.flatMap(Weather.init)
print(weather)
}
catch {
print(error)
}
- Respuesta anterior -
import Foundation
var jsonStr = "{/"weather/":[{/"id/":804,/"main/":/"Clouds/",/"description/":/"overcast clouds/",/"icon/":/"04d/"}],}"
var data = jsonStr.dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: false)
var localError: NSError?
var json: AnyObject! = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: &localError)
if let dict = json as? [String: AnyObject] {
if let weather = dict["weather"] as? [AnyObject] {
for dict2 in weather {
let id = dict2["id"]
let main = dict2["main"]
let description = dict2["description"]
println(id)
println(main)
println(description)
}
}
}
Como aún estoy recibiendo votos para esta respuesta, pensé que volvería a visitarla para Swift 2.0:
import Foundation
var jsonStr = "{/"weather/":[{/"id/":804,/"main/":/"Clouds/",/"description/":/"overcast clouds/",/"icon/":/"04d/"}],}"
var data = jsonStr.dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: false)
do {
var json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
if let dict = json as? [String: AnyObject] {
if let weather = dict["weather"] as? [AnyObject] {
for dict2 in weather {
let id = dict2["id"] as? Int
let main = dict2["main"] as? String
let description = dict2["description"] as? String
print(id)
print(main)
print(description)
}
}
}
}
catch {
print(error)
}
La mayor diferencia es que la variable json
ya no es un tipo opcional y la sintaxis do / try / catch. También seguí y escribí el id
, el main
y la description
.
Tratar:
Con eso puedes ir así:
let obj:[String:AnyObject] = [
"array": [JSON.null, false, 0, "", [], [:]],
"object":[
"null": JSON.null,
"bool": true,
"int": 42,
"double": 3.141592653589793,
"string": "a α/t弾/n𪚲",
"array": [],
"object": [:]
],
"url":"http://blog.livedoor.com/dankogai/"
]
let json = JSON(obj)
json.toString()
json["object"]["null"].asNull // NSNull()
json["object"]["bool"].asBool // true
json["object"]["int"].asInt // 42
json["object"]["double"].asDouble // 3.141592653589793
json["object"]["string"].asString // "a α/t弾/n𪚲"
json["array"][0].asNull // NSNull()
json["array"][1].asBool // false
json["array"][2].asInt // 0
json["array"][3].asString // ""
Usando mi biblioteca ( https://github.com/isair/JSONHelper ) puede hacer esto con su variable json de tipo AnyObject:
var weathers = [Weather]() // If deserialization fails, JSONHelper just keeps the old value in a non-optional variable. This lets you assign default values like this.
if let jsonDictionary = json as? JSONDictionary { // JSONDictionary is an alias for [String: AnyObject]
weathers <-- jsonDictionary["weather"]
}
Si su matriz no hubiera estado bajo la clave "clima", su código habría sido solo esto:
var weathers = [Weather]()
weathers <-- json
O si tiene una cadena json en sus manos, también puede pasarla, en lugar de crear primero un diccionario JSON a partir de la cadena. La única configuración que debe hacer es escribir una clase o estructura de clima:
struct Weather: Deserializable {
var id: String?
var name: String?
var description: String?
var icon: String?
init(data: [String: AnyObject]) {
id <-- data["id"]
name <-- data["name"]
description <-- data["description"]
icon <-- data["icon"]
}
}