¿Cómo deserializo una cadena JSON en un NSDictionary? (Para iOS 5+)

154

En mi aplicación iOS 5, tengo una NSStringque contiene una cadena JSON. Me gustaría deserializar esa representación de cadena JSON en un NSDictionaryobjeto nativo .

 "{\"password\" : \"1234\",  \"user\" : \"andreas\"}"

Intenté el siguiente enfoque:

NSDictionary *json = [NSJSONSerialization JSONObjectWithData:@"{\"2\":\"3\"}"
                                options:NSJSONReadingMutableContainers
                                  error:&e];  

Pero arroja un error de tiempo de ejecución. ¿Qué estoy haciendo mal?

-[__NSCFConstantString bytes]: unrecognized selector sent to instance 0x1372c 
*** Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '-[__NSCFConstantString bytes]: unrecognized selector sent to instance 0x1372c'
Andreas
fuente
Ese fue mi enfoque: NSDictionary * JSON = [NSJSONSerialization JSONObjectWithData: @ "{\" 2 \ ": \" 3 \ "}" opciones: NSJSONReadingMutableContainers error: & e]; obtengo: 2011-12-22 17: 18: 59.300 Pi9000 [938: 13803] - [__ NSCFConstantString bytes]: selector no reconocido enviado a la instancia 0x1372c 2011-12-22 17: 18: 59.302 Pi9000 [938: 13803] *** La aplicación finaliza debido a una excepción no detectada 'NSInvalidArgumentException', razón: '- [__ NSCFConstantString bytes]: selector no reconocido enviado a la instancia 0x1372c'
Andreas
Vea mi respuesta que muestra dos formas diferentes de deserializar una cadena JSON en un diccionario para Swift 3 y Swift 4.
Imanou Petit

Respuestas:

335

Parece que está pasando un NSStringparámetro donde debería pasar un NSDataparámetro:

NSError *jsonError;
NSData *objectData = [@"{\"2\":\"3\"}" dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:objectData
                                      options:NSJSONReadingMutableContainers 
                                        error:&jsonError];
Abizern
fuente
@Abizem, ¿qué error podría usar aquí? (Op no lo menciona)
Gracias ... este ayudó! y +1
Jayprakash Dubey
Gracias funcionó. Sin embargo, usando nilcomo error en lugar de &een XCode 5
Michael Ho Chum
3
Me gusta el objetivo C. Codificar su cadena en bytes sin procesar y luego decodificarlos nuevamente en NSStrings y NSNumbers. Esto es obvio, ¿no?
vahotm
1
@Abizern es común recibir JSON como una cadena desde algún lugar fuera de su aplicación
Chicowitz
37
NSData *data = [strChangetoJSON dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data
                                                             options:kNilOptions
                                                               error:&error];

Por ejemplo, tiene un NSStringcon caracteres especiales en NSStringstrChangetoJSON. Luego puede convertir esa cadena a respuesta JSON utilizando el código anterior.

Rosa del desierto
fuente
6

He hecho una categoría de @Abizern respuesta

@implementation NSString (Extensions)
- (NSDictionary *) json_StringToDictionary {
    NSError *error;
    NSData *objectData = [self dataUsingEncoding:NSUTF8StringEncoding];
    NSDictionary *json = [NSJSONSerialization JSONObjectWithData:objectData options:NSJSONReadingMutableContainers error:&error];
    return (!json ? nil : json);
}
@end

Úselo así,

NSString *jsonString = @"{\"2\":\"3\"}";
NSLog(@"%@",[jsonString json_StringToDictionary]);
Hemang
fuente
Entiendo que es una buena práctica no probar erroren estos casos, sino probar si el valor de retorno es nulo o no antes de regresar. es decir return json ?: nil; , un pequeño problema, pero vale la pena mencionarlo, creo.
Mike
@ Mike, creo que está bien comprobar si hay "error" independientemente del valor. Porque, si hay un error, volveremos de nilinmediato.
Hemang
De acuerdo con los documentos de Apple "Cuando se trata de errores pasados ​​por referencia, es importante probar el valor de retorno del método para ver si se produjo un error, como se muestra arriba. No solo pruebe para ver si el puntero de error estaba configurado para apuntar a un error." developer.apple.com/library/ios/documentation/Cocoa/Conceptual/… Creo que esto se debe a que podría haber casos en los que no se produce un error y el método devuelve un valor, pero la memoria a la que apunta el puntero de error es escrito para que pienses falsamente que existe un error.
Mike
Fui educado en una pregunta mía anterior: "La variable no está inicializada. Eso significa que el valor en esa dirección no está definido, por lo que cambiar el valor no significará nada ... Ya que no hay garantía de que el método no escriba basura en la dirección si no se produce un error, los documentos de Apple dicen que no es seguro probar el valor de la variable de error ". stackoverflow.com/questions/25558442/…
Mike
1
@ Mike, ¡genial, es bueno saberlo! Gracias por las referencias. Actualizaré esto pronto.
Hemang
5

Con Swift 3 y Swift 4, Stringtiene un método llamado data(using:allowLossyConversion:). data(using:allowLossyConversion:)tiene la siguiente declaración:

func data(using encoding: String.Encoding, allowLossyConversion: Bool = default) -> Data?

Devuelve un dato que contiene una representación de la cadena codificada usando una codificación dada.

Con Swift 4, String's data(using:allowLossyConversion:)se puede usar junto con JSONDecoder' s decode(_:from:)para deserializar una cadena JSON en un diccionario.

Además, con Swift 3 y Swift 4, los String's' data(using:allowLossyConversion:)también se pueden usar junto con JSONSerialization's' json​Object(with:​options:​)para deserializar una cadena JSON en un diccionario.


# 1 Solución Swift 4

Con Swift 4, JSONDecodertiene un método llamado decode(_:from:). decode(_:from:)tiene la siguiente declaración:

func decode<T>(_ type: T.Type, from data: Data) throws -> T where T : Decodable

Decodifica un valor de nivel superior del tipo dado a partir de la representación JSON dada.

El siguiente código de Playground muestra cómo usar data(using:allowLossyConversion:)y decode(_:from:)para obtener un Dictionaryformato JSON String:

let jsonString = """
{"password" : "1234",  "user" : "andreas"}
"""

if let data = jsonString.data(using: String.Encoding.utf8) {
    do {
        let decoder = JSONDecoder()
        let jsonDictionary = try decoder.decode(Dictionary<String, String>.self, from: data)
        print(jsonDictionary) // prints: ["user": "andreas", "password": "1234"]
    } catch {
        // Handle error
        print(error)
    }
}

# 2 Solución Swift 3 y Swift 4

Con Swift 3 y Swift 4, JSONSerializationtiene un método llamado json​Object(with:​options:​). json​Object(with:​options:​)tiene la siguiente declaración:

class func jsonObject(with data: Data, options opt: JSONSerialization.ReadingOptions = []) throws -> Any

Devuelve un objeto Foundation a partir de datos JSON dados.

El siguiente código de Playground muestra cómo usar data(using:allowLossyConversion:)y json​Object(with:​options:​)para obtener un Dictionaryformato JSON String:

import Foundation

let jsonString = "{\"password\" : \"1234\",  \"user\" : \"andreas\"}"

if let data = jsonString.data(using: String.Encoding.utf8) {
    do {
        let jsonDictionary = try JSONSerialization.jsonObject(with: data, options: []) as? [String : String]
        print(String(describing: jsonDictionary)) // prints: Optional(["user": "andreas", "password": "1234"])
    } catch {
        // Handle error
        print(error)
    }
}
Imanou Petit
fuente
3

Usando el código Abizern para swift 2.2

let objectData = responseString!.dataUsingEncoding(NSUTF8StringEncoding)
let json = try NSJSONSerialization.JSONObjectWithData(objectData!, options: NSJSONReadingOptions.MutableContainers)
IOS Singh
fuente