Jak deserializować ciąg JSON do NSDictionary? (Dla iOS 5+)

154

W mojej aplikacji na iOS 5 mam NSStringciąg znaków JSON. Chciałbym zdeserializować tę reprezentację ciągu JSON na NSDictionaryobiekt natywny .

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

Wypróbowałem następujące podejście:

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

Ale zgłasza błąd czasu wykonania. Co ja robię źle?

-[__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
źródło
Takie było moje podejście: NSDictionary * JSON = [NSJSONSerialization JSONObjectWithData: @ "{\" 2 \ ": \" 3 \ "}" opcje: NSJSONReadingMutableContainers błąd: & e]; dostaję: 2011-12-22 17: 18: 59.300 Pi9000 [938: 13803] - [__ NSCFConstantString bajtów]: nierozpoznany selektor wysłany do instancji 0x1372c 2011-12-22 17: 18: 59.302 Pi9000 [938: 13803] *** Zakończenie aplikacji z powodu niezłapanego wyjątku „NSInvalidArgumentException”, przyczyna: „- [__ NSCFConstantString bajtów]: nierozpoznany selektor wysłany do instancji 0x1372c”
Andreas
Zobacz moją odpowiedź , która pokazuje dwa różne sposoby deserializowania ciąg JSON do słownika dla Swift Swift 3 i 4.
imanou Petit

Odpowiedzi:

335

Wygląda na to, że przekazujesz NSStringparametr, do którego należy przekazywać NSDataparametr:

NSError *jsonError;
NSData *objectData = [@"{\"2\":\"3\"}" dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:objectData
                                      options:NSJSONReadingMutableContainers 
                                        error:&jsonError];
Abizern
źródło
@Abizem, jakiego błędu mogę tutaj użyć? (op nie wspomina o tym)
Dzięki ... ten pomógł! i +1
Jayprakash Dubey
Dzięki, zadziałało. Jednak użycie niljako błędu zamiast &ew XCode 5
Michael Ho Chum
3
Podoba mi się Objective C. Zakoduj ciąg do surowych bajtów, a następnie dekoduj je z powrotem do NSStrings i NSNumbers. To oczywiste, prawda?
vahotm
1
@Abizern często otrzymuje JSON jako ciąg znaków spoza aplikacji
Chicowitz,
37
NSData *data = [strChangetoJSON dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data
                                                             options:kNilOptions
                                                               error:&error];

Na przykład masz znak NSStringze znakami specjalnymi w NSStringstrChangetoJSON. Następnie możesz przekonwertować ten ciąg na odpowiedź JSON przy użyciu powyższego kodu.

Kwiat pustyni
źródło
6

Utworzyłem kategorię z odpowiedzi @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

Użyj tego w ten sposób,

NSString *jsonString = @"{\"2\":\"3\"}";
NSLog(@"%@",[jsonString json_StringToDictionary]);
Hemang
źródło
Rozumiem, że najlepszą praktyką jest nie testowanie errorw takich przypadkach, ale zamiast tego sprawdzenie, czy wartość zwracana jest równa zero, czy nie przed zwróceniem. tj. return json ?: nil; Drobne szczypanie, ale myślę, że warto o tym wspomnieć.
Mike,
@Mike, myślę, że można sprawdzić „błąd” niezależnie od wartości? Ponieważ jeśli wystąpi błąd, nilod razu wracamy .
Hemang
Zgodnie z dokumentacją firmy Apple „W przypadku błędów przekazywanych przez odwołanie ważne jest, aby przetestować zwracaną wartość metody, aby zobaczyć, czy wystąpił błąd, jak pokazano powyżej. Nie sprawdzaj tylko, czy wskaźnik błędu został ustawiony na błąd." developer.apple.com/library/ios/documentation/Cocoa/Conceptual/… Uważam, że dzieje się tak, ponieważ mogą wystąpić przypadki, w których błąd nie wystąpi, a metoda zwraca wartość, ale pamięć, na którą wskazuje wskaźnik błędu, jest do, więc błędnie myślisz, że istnieje błąd.
Mike
Uczyłem się w moim poprzednim pytaniu: „Zmienna nie jest zainicjowana. Oznacza to, że wartość pod tym adresem jest niezdefiniowana, więc zmiana wartości nic nie znaczy ... Ponieważ nie ma gwarancji, że metoda nie zapisze śmieci do adresu, jeśli błąd nie wystąpi, dokumentacja Apple twierdzi, że testowanie wartości zmiennej błędu jest niebezpieczne ”. stackoverflow.com/questions/25558442/…
Mike
1
@Mike, och, świetnie, dobrze wiedzieć! Dzięki za referencje. Niedługo to zaktualizuję.
Hemang,
5

W Swift 3 i Swift 4 Stringma metodę o nazwie data(using:allowLossyConversion:). data(using:allowLossyConversion:)posiada następującą deklarację:

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

Zwraca dane zawierające reprezentację String zakodowaną przy użyciu danego kodowania.

W Swift 4, String's data(using:allowLossyConversion:)można używać w połączeniu z JSONDecoder' s decode(_:from:)w celu deserializacji ciągu JSON do słownika.

Ponadto szybkimi 3 i Swift 4 String„y data(using:allowLossyConversion:)mogą być również stosowane w połączeniu z JSONSerialization” S json​Object(with:​options:​)w celu Cofnięcie ciąg JSON w słowniku.


# 1. Rozwiązanie Swift 4

W Swift 4 JSONDecoderma metodę o nazwie decode(_:from:). decode(_:from:)posiada następującą deklarację:

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

Dekoduje wartość najwyższego poziomu danego typu z danej reprezentacji JSON.

Poniższy kod Playground pokazuje, jak używać data(using:allowLossyConversion:)i decode(_:from:)jak uzyskać a Dictionaryz formatu 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. Rozwiązanie Swift 3 i Swift 4

W Swift 3 i Swift 4 JSONSerializationma metodę o nazwie json​Object(with:​options:​). json​Object(with:​options:​)posiada następującą deklarację:

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

Zwraca obiekt Foundation z podanych danych JSON.

Poniższy kod Playground pokazuje, jak używać data(using:allowLossyConversion:)i json​Object(with:​options:​)jak uzyskać a Dictionaryz formatu 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
źródło
3

Używanie kodu Abizern dla Swift 2.2

let objectData = responseString!.dataUsingEncoding(NSUTF8StringEncoding)
let json = try NSJSONSerialization.JSONObjectWithData(objectData!, options: NSJSONReadingOptions.MutableContainers)
IOS Singh
źródło