Como desserializar uma string JSON em um NSDictionary? (Para iOS 5 ou superior)

154

No meu aplicativo iOS 5, eu tenho um NSStringque contém uma string JSON. Eu gostaria de desserializar essa representação de string JSON em um NSDictionaryobjeto nativo .

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

Eu tentei a seguinte abordagem:

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

Mas isso gera um erro de tempo de execução. O que estou fazendo de errado?

-[__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
fonte
Essa foi a minha abordagem: NSDictionary * JSON = [NSJSONSerialization JSONObjectWithData: @ "{\" 2 \ ": \" 3 \ "}" opções: erro de NSJSONReadingMutableContainers: & e]; eu recebo: 2011-12-22 17: 18: 59.300 Pi9000 [938: 13803] - [__ NSCFConstantString bytes]: seletor não reconhecido enviado para a instância 0x1372c 2011-12-22 17: 18: 59.302 Pi9000 [938: 13803] *** Finalizando o aplicativo devido à exceção não capturada 'NSInvalidArgumentException', motivo: '- [__ NSCFConstantString bytes]: seletor não reconhecido enviado para a instância 0x1372c'
Andreas
Veja a minha resposta que mostra duas maneiras diferentes para desserializar uma string JSON em um dicionário para Swift 3 e 4. Swift
Imanou Petit

Respostas:

335

Parece que você está passando um NSStringparâmetro no qual deveria estar passando um NSDataparâmetro:

NSError *jsonError;
NSData *objectData = [@"{\"2\":\"3\"}" dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:objectData
                                      options:NSJSONReadingMutableContainers 
                                        error:&jsonError];
Abizern
fonte
@ Abizem, que erro eu poderia usar aqui? (op não mencioná-lo)
Obrigado ... este ajudou! e +1
Jayprakash Dubey
Obrigado, funcionou. No entanto, usando nilcomo erro em vez de &eno XCode 5
Michael Ho Chum
3
Eu gosto do objetivo C. Codifique sua string para bytes não processados ​​e decodifique-os novamente para NSStrings e NSNumbers. Isso é óbvio, não é?
vahotm
1
@Abizern sua comum receber JSON como uma string de fora em algum lugar de sua aplicação
Chicowitz
37
NSData *data = [strChangetoJSON dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data
                                                             options:kNilOptions
                                                               error:&error];

Por exemplo, você possui um NSStringcaractere especial em NSStringstrChangetoJSON. Em seguida, você pode converter essa sequência em resposta JSON usando o código acima.

Rosa do Deserto
fonte
6

Eu fiz uma categoria da resposta @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

Use-o assim,

NSString *jsonString = @"{\"2\":\"3\"}";
NSLog(@"%@",[jsonString json_StringToDictionary]);
Hemang
fonte
Entendo que é uma boa prática não testar errornesses casos, mas testar se o valor de retorno é nulo ou não antes de retornar. ou seja return json ?: nil; , nitpick menor, mas vale a pena mencionar, eu acho.
Mike
@ Mike, acho que não há problema em verificar se há "erro", independentemente do valor? Porque, se houver um erro, estamos retornando nilimediatamente.
Hemang
De acordo com os documentos da Apple "Ao lidar com erros passados ​​por referência, é importante testar o valor de retorno do método para verificar se ocorreu um erro, como mostrado acima. Não basta testar para ver se o ponteiro de erro foi definido como um erro." developer.apple.com/library/ios/documentation/Cocoa/Conceptual/… Acredito que isso ocorre porque pode haver casos em que um erro não ocorre e o método retorna um valor, mas a memória para onde o ponteiro do erro aponta é gravado em, para que você pense falsamente que existe um erro.
Mike
Eu fui educado em uma pergunta anterior minha: "A variável não foi inicializada. Isso significa que o valor nesse endereço é indefinido, portanto, alterar o valor não significa nada ... Como não há garantia de que o método não será escrito lixo no endereço, se não ocorrer um erro, os documentos da Apple dizem que não é seguro testar o valor da variável de erro ". stackoverflow.com/questions/25558442/…
Mike
1
@ Mike, ótimo, bom saber! Obrigado pelas referências. Vou atualizar isso em breve.
Hemang
5

Com o Swift 3 e o Swift 4, o Stringmétodo é chamado data(using:allowLossyConversion:). data(using:allowLossyConversion:)tem a seguinte declaração:

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

Retorna um dado contendo uma representação da String codificada usando uma determinada codificação.

Com o Swift 4, String's' data(using:allowLossyConversion:)podem ser usados ​​em conjunto com JSONDecoder's decode(_:from:)para desserializar uma string JSON em um dicionário.

Além disso, com Swift 3 e Swift 4, Stringos s data(using:allowLossyConversion:)também podem ser usados ​​em conjunto com JSONSerializationos json​Object(with:​options:​)para desserializar uma string JSON em um dicionário.


# 1 Solução Swift 4

Com o Swift 4, JSONDecodertem um método chamado decode(_:from:). decode(_:from:)tem a seguinte declaração:

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

Decodifica um valor de nível superior do tipo especificado a partir da representação JSON fornecida.

O código do Playground abaixo mostra como usar data(using:allowLossyConversion:)e decode(_:from:)para obter a Dictionarypartir de um formato 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 Solução Swift 3 e Swift 4

Com o Swift 3 e o Swift 4, o JSONSerializationmétodo é chamado json​Object(with:​options:​). json​Object(with:​options:​)tem a seguinte declaração:

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

Retorna um objeto Foundation a partir de dados JSON fornecidos.

O código do Playground abaixo mostra como usar data(using:allowLossyConversion:)e json​Object(with:​options:​)para obter a Dictionarypartir de um formato 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
fonte
3

Usando o código Abizern para o swift 2.2

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