Як користуватися NSJSONSerialization


156

У мене є рядок JSON (з PHP, json_encode()який виглядає приблизно так:

[{"id": "1", "name":"Aaa"}, {"id": "2", "name":"Bbb"}]

Я хочу проаналізувати це в якійсь структурі даних для мого додатка iPhone. Я думаю, що найкраще для мене було б мати масив словників, тож 0-й елемент у масиві - це словник з ключами "id" => "1"та "name" => "Aaa".

Я не розумію, як NSJSONSerializationзберігаються дані. Ось мій код поки що:

NSError *e = nil;
NSDictionary *JSON = [NSJSONSerialization 
    JSONObjectWithData: data 
    options: NSJSONReadingMutableContainers 
    error: &e];

Це якраз те, що я бачив як приклад на іншому веб-сайті. Я намагався прочитати на JSONоб’єкті, надрукувавши кількість елементів і подібних речей, але я завжди отримую EXC_BAD_ACCESS.

Як я можу NSJSONSerializationрозібрати JSON вище та перетворити його в структуру даних, яку я згадав?


ваша змінна даних , ймовірно, нульова
d.lebedev

Це не так, я це вже перевірив.
Логан Серман

Ви намагалися перевірити, чи є якась відповідна інформація в об’єкті помилки?
Моноло

Відповіді:


214

Ваш об'єкт root json - це не словник, а масив:

[{"id": "1", "name":"Aaa"}, {"id": "2", "name":"Bbb"}]

Це може дати вам чітку картину, як впоратися з цим:

NSError *e = nil;
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData: data options: NSJSONReadingMutableContainers error: &e];

if (!jsonArray) {
  NSLog(@"Error parsing JSON: %@", e);
} else {
   for(NSDictionary *item in jsonArray) {
      NSLog(@"Item: %@", item);
   }
}

Дякую, я спробую це, але не варто [JSON count]повертати щось замість того, щоб просто дати мені EXC_BAD_ACCESS?
Логан Серман

Слід, тому я додав чек, якщо !jsonArrayі надрукував помилку. Це повинно відображати будь-яку помилку, що сталася під час розбору.
rckoenes

1
@ xs2bush ні, оскільки ви не створили його, jsonArrayвоно повинно бути автоматичним випуском.
rckoenes

@Logan: Так, [count JSON] повинен повернути значення. Дивіться мою відповідь нижче щодо зомбі. EXC_BAD_ACCESS майже завжди пов'язаний із зомбі.
Олі

У цьому випадку елемент є ключем у заданій парі значень ключа JSON. Однак я вже знаю ключ для значення, яке я хочу, а саме "ключ". Мої зусилля, щоб отримати значення цього ключа і вивести його в журнал, зазнали невдачі. Будь-яке подальше розуміння?
Thomas Clowes

75

Це мій код для перевірки, чи отриманий json є масивом чи словником:

NSError *jsonError = nil;
id jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&jsonError];

if ([jsonObject isKindOfClass:[NSArray class]]) {
    NSLog(@"its an array!");
    NSArray *jsonArray = (NSArray *)jsonObject;
    NSLog(@"jsonArray - %@",jsonArray);
}
else {
    NSLog(@"its probably a dictionary");
    NSDictionary *jsonDictionary = (NSDictionary *)jsonObject;
    NSLog(@"jsonDictionary - %@",jsonDictionary);
}

Я спробував це для параметрів: kNilOptions і NSJSONReadingMutableContainers і працює правильно для обох.

Очевидно, що фактичний код не може бути таким чином, коли я створюю NSArray або NSDictionary вказівник у блоці if-else.


29

Це працює для мене. Ваш dataоб’єкт, ймовірно, nilі, як зазначав rckoenes, кореневим об'єктом повинен бути (змінний) масив. Дивіться цей код:

NSString *jsonString = @"[{\"id\": \"1\", \"name\":\"Aaa\"}, {\"id\": \"2\", \"name\":\"Bbb\"}]";
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *e = nil;
NSMutableArray *json = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&e];
NSLog(@"%@", json);

(Мені довелося уникати лапок у рядку JSON із зворотними нахилами.)


9

Ваш код здається прекрасним, за винятком того, що результат - це NSArrayне, а неNSDictionary , ось приклад:

Перші два рядки просто створюють об'єкт даних за допомогою JSON, такий самий, як і ви його читали з мережі.

NSString *jsonString = @"[{\"id\": \"1\", \"name\":\"Aaa\"}, {\"id\": \"2\", \"name\":\"Bbb\"}]";
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];

NSError *e;
NSMutableArray *jsonList = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&e];
NSLog(@"jsonList: %@", jsonList);

Вміст NSLog (список словників):

jsonList: (
           {
               id = 1;
               name = Aaa;
           },
           {
               id = 2;
               name = Bbb;
           }
           )

Що означає цей параметр (NSJSONReadingMutableContainers). Я не kNilOption і все працює добре. Скажіть мені, як скористатися цією опцією
Zar E Ahmer

Найбільше звернення в Google: NSJSONReadingMutableLeaves"Вказує, що рядки аркушів у графіку об'єкта JSON створюються як екземпляри NSMutableString."
зап

а як щодо MutableContainer
Zar E Ahmer

На жаль, знову від верхнього результату Google NSJSONReadingMutableContainers: "Вказує, що масиви та словники створюються як об'єкти, що змінюються."
зап

1
Вони допомагають лише в тому випадку, якщо ви плануєте змінити повернутий об'єкт JSON і зберегти його назад. У будь-якому випадку об'єкти - це, мабуть, авторизовані об'єкти, і це, здається, є першопричиною.
Діпак Г.М.

6
[{"id": "1", "name":"Aaa"}, {"id": "2", "name":"Bbb"}]

Наведені вище дані JSON показують, що у нас є масив, що містить кількість словників.

Для аналізу потрібно використовувати цей код:

NSError *e = nil;
NSArray *JSONarray = [NSJSONSerialization JSONObjectWithData: data options: NSJSONReadingMutableContainers error: &e];
        for(int i=0;i<[JSONarray count];i++)
        {
            NSLog(@"%@",[[JSONarray objectAtIndex:i]objectForKey:@"id"]);
             NSLog(@"%@",[[JSONarray objectAtIndex:i]objectForKey:@"name"]);
        }

Для швидких 3/3 +

   //Pass The response data & get the Array
    let jsonData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [AnyObject]
    print(jsonData)
    // considering we are going to get array of dictionary from url

    for  item  in jsonData {
        let dictInfo = item as! [String:AnyObject]
        print(dictInfo["id"])
        print(dictInfo["name"])
    }

3

Наступний код отримує об'єкт JSON з веб-сервера та аналізує його на NSDictionary. Я використовував API openweathermap, який повертає простий відповідь JSON для цього прикладу. Щоб зробити його простим, цей код використовує синхронні запити.

   NSString *urlString   = @"http://api.openweathermap.org/data/2.5/weather?q=London,uk"; // The Openweathermap JSON responder
   NSURL *url            = [[NSURL alloc]initWithString:urlString];
   NSURLRequest *request = [NSURLRequest requestWithURL:url];
   NSURLResponse *response;
   NSData *GETReply      = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
   NSDictionary *res     = [NSJSONSerialization JSONObjectWithData:GETReply options:NSJSONReadingMutableLeaves|| NSJSONReadingMutableContainers error:nil];
   Nslog(@"%@",res);

Я думаю, що ваша відповідь повинна бути найкращою відповіддю, оскільки це здається найшвидшим способом доступу до структури JSON.
Поризм

2
Параметри не повинні використовувати два | але єдиний | оскільки вони повинні бути побітними АБО.
Deepak GM

Питання нічого не задає про мережеві запити
Ной Гілмор

2

@rckoenes вже показав, як правильно отримати свої дані з рядка JSON.

До питання, яке ви задали: EXC_BAD_ACCESSмайже завжди виникає, коли ви намагаєтесь отримати доступ до об'єкта після його [авто-] звільнення. Це не характерно для [де-] серіалізації JSON, але, скоріше, стосується того, як ви отримаєте об’єкт і потім отримаєте доступ до нього після його звільнення. Той факт, що він прийшов через JSON, не має значення.

Існує багато-багато сторінок, що описують, як налагодити це - ви хочете, щоб Google (або ТАК), obj-c zombie objectsі, зокрема NSZombieEnabled, що виявиться вам безцінним, допомагаючи визначити джерело ваших зомбі-об'єктів. ("Zombie" - це називається, коли ви випускаєте об'єкт, але зберігайте вказівник на нього і намагайтеся посилатися на нього пізніше.)


1

Swift 2.0 на Xcode 7 (бета-версія) з блоком do / try / catch:

// MARK: NSURLConnectionDataDelegate

func connectionDidFinishLoading(connection:NSURLConnection) {
  do {
    if let response:NSDictionary = try NSJSONSerialization.JSONObjectWithData(receivedData, options:NSJSONReadingOptions.MutableContainers) as? Dictionary<String, AnyObject> {
      print(response)
    } else {
      print("Failed...")
    }
  } catch let serializationError as NSError {
    print(serializationError)
  }
}

1

ПРИМІТКА: Для Swift 3 . Ваш рядок JSON повертає масив замість словника. Будь ласка, спробуйте наступне:

        //Your JSON String to be parsed
        let jsonString = "[{\"id\": \"1\", \"name\":\"Aaa\"}, {\"id\": \"2\", \"name\":\"Bbb\"}]";

        //Converting Json String to NSData
        let data = jsonString.data(using: .utf8)

        do {

            //Parsing data & get the Array
            let jsonData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [AnyObject]

            //Print the whole array object
            print(jsonData)

            //Get the first object of the Array
            let firstPerson = jsonData[0] as! [String:Any]

            //Looping the (key,value) of first object
            for (key, value) in firstPerson {
                //Print the (key,value)
                print("\(key) - \(value) ")
            }

        } catch let error as NSError {
            //Print the error
            print(error)
        }

0
#import "homeViewController.h"
#import "detailViewController.h"

@interface homeViewController ()

@end

@implementation homeViewController

- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.tableView.frame = CGRectMake(0, 20, 320, 548);
    self.title=@"Jason Assignment";

    // Uncomment the following line to preserve selection between presentations.
    // self.clearsSelectionOnViewWillAppear = NO;

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem;
    [self clientServerCommunication];
}

-(void)clientServerCommunication
{
    NSURL *url = [NSURL URLWithString:@"http://182.72.122.106/iphonetest/getTheData.php"];
    NSURLRequest *req = [NSURLRequest requestWithURL:url];
    NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:req delegate:self];
    if (connection)
    {
        webData = [[NSMutableData alloc]init];
    }
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    [webData setLength:0];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    [webData appendData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSDictionary *responseDict = [NSJSONSerialization JSONObjectWithData:webData options:0 error:nil];

    /*Third party API
     NSString *respStr = [[NSString alloc]initWithData:webData encoding:NSUTF8StringEncoding];
     SBJsonParser *objSBJson = [[SBJsonParser alloc]init];
     NSDictionary *responseDict = [objSBJson objectWithString:respStr]; */
    resultArray = [[NSArray alloc]initWithArray:[responseDict valueForKey:@"result"]];
    NSLog(@"resultArray: %@",resultArray);
    [self.tableView reloadData];
}


- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
//#warning Potentially incomplete method implementation.
    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//#warning Incomplete method implementation.
    // Return the number of rows in the section.
    return [resultArray count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    }

    // Configure the cell...
    cell.textLabel.text = [[resultArray objectAtIndex:indexPath.row] valueForKey:@"name"];
    cell.detailTextLabel.text = [[resultArray objectAtIndex:indexPath.row] valueForKey:@"designation"];

    NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[resultArray objectAtIndex:indexPath.row] valueForKey:@"image"]]];
cell.imageview.image = [UIImage imageWithData:imageData];

    return cell;
}

/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Return NO if you do not want the specified item to be editable.
    return YES;
}
*/

/*
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        // Delete the row from the data source
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
    }   
    else if (editingStyle == UITableViewCellEditingStyleInsert) {
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
    }   
}
*/

/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/

/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Return NO if you do not want the item to be re-orderable.
    return YES;
}
*/


#pragma mark - Table view delegate

// In a xib-based application, navigation from a table can be handled in -tableView:didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Navigation logic may go here, for example:
     //Create the next view controller.
    detailViewController *detailViewController1 = [[detailViewController alloc]initWithNibName:@"detailViewController" bundle:nil];

 //detailViewController *detailViewController = [[detailViewController alloc] initWithNibName:@"detailViewController" bundle:nil];

 // Pass the selected object to the new view controller.

 // Push the view controller.
 detailViewController1.nextDict = [[NSDictionary alloc]initWithDictionary:[resultArray objectAtIndex:indexPath.row]];
 [self.navigationController pushViewController:detailViewController1 animated:YES];

    // Pass the selected object to the new view controller.

    // Push the view controller.
  //  [self.navigationController pushViewController:detailViewController animated:YES];
}



@end

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    empName.text=[nextDict valueForKey:@"name"];
    deptlbl.text=[nextDict valueForKey:@"department"];
    designationLbl.text=[nextDict valueForKey:@"designation"];
    idLbl.text=[nextDict valueForKey:@"id"];
    salaryLbl.text=[nextDict valueForKey:@"salary"];
    NSString *ImageURL = [nextDict valueForKey:@"image"];
    NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:ImageURL]];
    image.image = [UIImage imageWithData:imageData];
}

0

Здається, проблема полягає в автоматичному випуску об'єктів. NSJSONSerialization JSONObjectWithData очевидно створює деякі автоматично випущені об'єкти і передає їх вам назад. Якщо ви спробуєте перенести це на інший потік, воно не вийде, оскільки його неможливо розмістити на іншому потоці.

Трюком може бути спробувати зробити змінну копію цього словника або масиву і використовувати його.

NSError *e = nil;
id jsonObject = [NSJSONSerialization 
JSONObjectWithData: data 
options: NSJSONReadingMutableContainers 
error: &e] mutableCopy];

Якщо трактувати NSD Dictionary як NSArray, це не призведе до виключення з поганим доступом, але натомість, швидше за все, вийде з ладу при здійсненні виклику методу.

Також можливі варіанти тут не дуже важливі, але краще дати NSJSONReadingMutableContainers | NSJSONReadingMutableContainers | NSJSONReadingAllowFragments, але навіть якщо вони є автоматично випущеними об'єктами, це може не вирішити цю проблему.


Діпак, ти двічі перераховував NSJSONReadingMutableContainers. Ти мав на увазі, щоб хтось був NSJSONReadingMutableLeaves?
jk7

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.