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.