Comment désérialiser une chaîne JSON dans un NSDictionary? (Pour iOS 5+)

154

Dans mon application iOS 5, j'ai un NSStringqui contient une chaîne JSON. Je voudrais désérialiser cette représentation de chaîne JSON en un NSDictionaryobjet natif .

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

J'ai essayé l'approche suivante:

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

Mais cela génère une erreur d'exécution. Qu'est-ce que je fais 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
la source
C'était mon approche: NSDictionary * JSON = [NSJSONSerialization JSONObjectWithData: @ "{\" 2 \ ": \" 3 \ "}" options: NSJSONReadingMutableContainers error: & e]; j'obtiens: 2011-12-22 17: 18: 59.300 Pi9000 [938: 13803] - [__ NSCFConstantString bytes]: sélecteur non reconnu envoyé à l'instance 0x1372c 2011-12-22 17: 18: 59.302 Pi9000 [938: 13803] *** Arrêt de l'application en raison d'une exception non interceptée 'NSInvalidArgumentException', raison: '- [__ NSCFConstantString bytes]: sélecteur non reconnu envoyé à l'instance 0x1372c'
Andreas
Voir ma réponse qui montre deux façons différentes de désérialiser une chaîne JSON dans un dictionnaire pour Swift 3 et Swift 4.
Imanou Petit

Réponses:

335

Il semble que vous passiez un NSStringparamètre où vous devriez passer un NSDataparamètre:

NSError *jsonError;
NSData *objectData = [@"{\"2\":\"3\"}" dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:objectData
                                      options:NSJSONReadingMutableContainers 
                                        error:&jsonError];
Abizern
la source
@Abizem, quelle erreur puis-je utiliser ici? (op ne le mentionne pas)
Merci ... celui-ci a aidé! et +1
Jayprakash Dubey
Merci, cela a fonctionné. Cependant, en utilisant nilcomme erreur au lieu de &eXCode 5
Michael Ho Chum
3
J'aime Objective C. Encodez votre chaîne en octets bruts, puis décodez-les en NSStrings et NSNumbers. C'est évident, n'est-ce pas?
vahotm
1
@Abizern il est courant de recevoir JSON sous forme de chaîne de quelque part en dehors de votre application
Chicowitz
37
NSData *data = [strChangetoJSON dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data
                                                             options:kNilOptions
                                                               error:&error];

Par exemple, vous avez un NSStringavec des caractères spéciaux dans NSStringstrChangetoJSON. Ensuite, vous pouvez convertir cette chaîne en réponse JSON en utilisant le code ci-dessus.

Rose du désert
la source
6

J'ai créé une catégorie à partir de la réponse @Abizern

@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

Utilisez-le comme ça,

NSString *jsonString = @"{\"2\":\"3\"}";
NSLog(@"%@",[jsonString json_StringToDictionary]);
Hemang
la source
Je crois comprendre qu'il est préférable de ne pas tester errordans ces cas, mais plutôt de tester si la valeur de retour est nulle ou non avant de retourner. c'est-à-dire return json ?: nil; mineure pinaille, mais mérite d'être mentionnée, je pense.
Mike le
@Mike, je pense que c'est correct de vérifier "erreur" quelle que soit la valeur? Parce que s'il y a une erreur, nous revenons niltout de suite.
Hemang le
Selon la documentation d'Apple, "Lors du traitement des erreurs transmises par référence, il est important de tester la valeur de retour de la méthode pour voir si une erreur s'est produite, comme indiqué ci-dessus. Ne vous contentez pas de tester pour voir si le pointeur d'erreur a été défini pour pointer vers une erreur." developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ ... Je pense que c'est parce qu'il pourrait y avoir des cas où une erreur ne se produit pas et la méthode renvoie une valeur, mais la mémoire où le pointeur d'erreur pointe est écrit à, donc vous pensez à tort qu'une erreur existe.
Mike
J'ai été formé à une de mes questions précédentes: "La variable n'est pas initialisée. Cela signifie que la valeur à cette adresse n'est pas définie, donc changer la valeur ne veut rien dire ... Comme il n'y a aucune garantie que la méthode n'écrira pas garbage dans l'adresse si une erreur ne se produit pas, les documents d'Apple indiquent qu'il n'est pas sûr de tester la valeur de la variable d'erreur. " stackoverflow.com/questions/25558442/…
Mike
1
@Mike, oh génial, bon à savoir! Merci pour les références. Je mettrai à jour ceci bientôt.
Hemang
5

Avec Swift 3 et Swift 4, Stringa une méthode appelée data(using:allowLossyConversion:). data(using:allowLossyConversion:)a la déclaration suivante:

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

Renvoie un Data contenant une représentation de la chaîne encodée à l'aide d'un encodage donné.

Avec Swift 4, String's data(using:allowLossyConversion:)peut être utilisé avec JSONDecoder' sdecode(_:from:) afin de désérialiser une chaîne JSON dans un dictionnaire.

De plus, avec Swift 3 et Swift 4, String's data(using:allowLossyConversion:)peut également être utilisé en conjonction avec JSONSerialization' s json​Object(with:​options:​)afin de désérialiser une chaîne JSON dans un dictionnaire.


#1. Solution Swift 4

Avec Swift 4, JSONDecodera une méthode appelée decode(_:from:). decode(_:from:)a la déclaration suivante:

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

Décode une valeur de niveau supérieur du type donné à partir de la représentation JSON donnée.

Le code Playground ci-dessous montre comment utiliser data(using:allowLossyConversion:)et decode(_:from:)pour obtenir un à Dictionarypartir d'un format 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. Solution Swift 3 et Swift 4

Avec Swift 3 et Swift 4, JSONSerializationa une méthode appelée json​Object(with:​options:​). json​Object(with:​options:​)a la déclaration suivante:

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

Renvoie un objet Foundation à partir de données JSON données.

Le code Playground ci-dessous montre comment utiliser data(using:allowLossyConversion:)et json​Object(with:​options:​)pour obtenir un à Dictionarypartir d'un format 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
la source
3

Utilisation du code Abizern pour Swift 2.2

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