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'

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];

@ 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.


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]);

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)
    }
}

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)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.