Как десериализовать строку JSON в NSDictionary? (Для iOS 5+)

154

В моем приложении для iOS 5 есть NSStringстрока JSON. Я хотел бы десериализовать это строковое представление JSON в нативный NSDictionaryобъект.

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

Я попробовал следующий подход:

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

Но он выдает ошибку во время выполнения. Что я делаю не так?

-[__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
источник
Это был мой подход: NSDictionary * JSON = [NSJSONSerialization JSONObjectWithData: @ "{\" 2 \ ": \" 3 \ "}" options: NSJSONReadingMutableContainers error: & e]; я получаю: 2011-12-22 17: 18: 59.300 Pi9000 [938: 13803] - [__ NSCFConstantString bytes]: нераспознанный селектор отправлен в экземпляр 0x1372c 2011-12-22 17: 18: 59.302 Pi9000 [938: 13803] *** Завершение работы приложения из-за необработанного исключения «NSInvalidArgumentException», причина: «- [__ NSCFConstantString bytes]: нераспознанный селектор отправлен в экземпляр 0x1372c»
Андреас
Смотрите мой ответ, который показывает два разных способа десериализации строки JSON в словарь для Swift 3 и Swift 4.
Imanou Petit

Ответы:

335

Похоже, вы передаете NSStringпараметр, где вы должны передать NSDataпараметр:

NSError *jsonError;
NSData *objectData = [@"{\"2\":\"3\"}" dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:objectData
                                      options:NSJSONReadingMutableContainers 
                                        error:&jsonError];
Abizern
источник
@Abizem, какую ошибку я могу использовать здесь? (оп не упомянул)
Спасибо ... это помогло! и +1
Джайпракаш Дубей
Спасибо, это сработало. Тем не менее, в nilкачестве ошибки вместо &eXCode 5
Michael Ho Chum
3
Мне нравится Цель C. Кодировать вашу строку в необработанные байты, а затем декодировать их обратно в NSStrings и NSNumbers. Это очевидно, не так ли?
Вахотм
1
@Abizern принято получать JSON в виде строки откуда-то за пределами вашего приложения
Chicowitz,
37
NSData *data = [strChangetoJSON dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data
                                                             options:kNilOptions
                                                               error:&error];

Например, у вас есть NSStringспециальные символы в NSStringstrChangetoJSON. Затем вы можете преобразовать эту строку в ответ JSON, используя приведенный выше код.

Пустынная роза
источник
6

Я сделал категорию из ответа @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

Используйте это так,

NSString *jsonString = @"{\"2\":\"3\"}";
NSLog(@"%@",[jsonString json_StringToDictionary]);
Hemang
источник
Насколько я понимаю, лучше не проверять errorв этих случаях, а вместо этого проверять, является ли возвращаемое значение нулевым или нет перед возвратом. т. е. return json ?: nil; мелкий придира, но стоит упомянуть, я думаю.
Майк
@ Майк, я думаю, что можно проверить «ошибку» независимо от значения? Потому что, если есть ошибка, мы сразу же возвращаемся nil.
Hemang
Согласно документации Apple: «При работе с ошибками, передаваемыми по ссылке, важно проверить возвращаемое значение метода, чтобы увидеть, произошла ли ошибка, как показано выше. Не просто проверяйте, установлен ли указатель ошибки на ошибка." developer.apple.com/library/ios/documentation/Cocoa/Conceptual/… Я полагаю, это потому, что могут быть случаи, когда ошибка не возникает, и метод возвращает значение, но память, на которую указывает указатель ошибки, написано, так что вы ошибочно думаете, что существует ошибка.
Майк
Я был обучен в моем предыдущем вопросе: «Переменная неинициализирована. Это означает, что значение по этому адресу не определено, поэтому изменение значения ничего не будет значить ... Так как нет гарантии, что метод не будет писать мусор в адрес, если ошибка не возникает, документы Apple говорят, что проверять значение переменной error небезопасно ». stackoverflow.com/questions/25558442/…
Майк
1
@ Майк, о, здорово, приятно знать! Спасибо за ссылки. Я скоро обновлю это.
Hemang
5

С Swift 3 и Swift 4, Stringесть метод, который называется data(using:allowLossyConversion:). data(using:allowLossyConversion:)имеет следующую декларацию:

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

Возвращает данные, содержащие представление строки, закодированной с использованием заданной кодировки.

В Swift 4 можно использовать Strings data(using:allowLossyConversion:)вместе с JSONDecoders decode(_:from:)для десериализации строки JSON в словарь.

Кроме того, с Swift 3 и Swift 4, Strings data(using:allowLossyConversion:)также может использоваться вместе с JSONSerializations json​Object(with:​options:​)для десериализации строки JSON в словарь.


# 1. Swift 4 решение

В Swift 4 JSONDecoderесть метод с именем decode(_:from:). decode(_:from:)имеет следующую декларацию:

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

Декодирует значение верхнего уровня данного типа из заданного представления JSON.

Код Playground ниже показывает, как использовать data(using:allowLossyConversion:)и decode(_:from:)чтобы получить Dictionaryформат в формате 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. Swift 3 и Swift 4 решение

С Swift 3 и Swift 4, JSONSerializationесть метод, который называется json​Object(with:​options:​). json​Object(with:​options:​)имеет следующую декларацию:

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

Возвращает объект Foundation из заданных данных JSON.

Код Playground ниже показывает, как использовать data(using:allowLossyConversion:)и json​Object(with:​options:​)чтобы получить Dictionaryформат в формате 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
источник
3

Использование кода Abizern для Swift 2.2

let objectData = responseString!.dataUsingEncoding(NSUTF8StringEncoding)
let json = try NSJSONSerialization.JSONObjectWithData(objectData!, options: NSJSONReadingOptions.MutableContainers)
IOS Singh
источник