Como usar o NSJSONSerialization

156

Eu tenho uma string JSON (do PHP json_encode()que se parece com isso:

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

Quero analisar isso em algum tipo de estrutura de dados para o meu aplicativo para iPhone. Eu acho que a melhor coisa para mim seria ter uma matriz de dicionários, então o elemento 0 da matriz é um dicionário com chaves "id" => "1"e "name" => "Aaa".

Eu não entendo como as NSJSONSerializationlojas armazenam os dados. Aqui esta o meu codigo ate agora:

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

Isso é apenas algo que eu vi como exemplo em outro site. Eu tenho tentado ler o JSONobjeto imprimindo o número de elementos e coisas assim, mas estou sempre conseguindo EXC_BAD_ACCESS.

Como uso NSJSONSerializationpara analisar o JSON acima e transformá-lo na estrutura de dados que mencionei?

Logan Serman
fonte
sua dados variável é provavelmente nula
d.lebedev
Não é, eu já testei isso.
Logan Serman
Você tentou ver se há alguma informação relevante no objeto de erro?
Monolo

Respostas:

214

Seu objeto json raiz não é um dicionário, mas uma matriz:

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

Isso pode lhe dar uma imagem clara de como lidar com isso:

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);
   }
}
rckoenes
fonte
Obrigado, vou tentar isso, mas não deve [JSON count]retornar algo em vez de apenas me dar EXC_BAD_ACCESS?
Logan Serman
Por isso, adicionei a verificação se !jsonArraye imprimi o erro. Isso deve exibir qualquer erro que ocorreu durante a análise.
precisa saber é o seguinte
1
@ xs2bush não, já que você não criou o arquivo, jsonArraydeve ser autorelease.
Rckoenes #
@Logan: Sim, [JSON count] deve retornar um valor. Veja minha resposta abaixo sobre zumbis. EXC_BAD_ACCESS é quase sempre relacionado a zumbis.
Olie
Nesse caso, item é a chave em um determinado par de valores de chave JSON. Seu loop for funciona perfeitamente na saída de cada uma das minhas chaves JSON. No entanto, eu já sei a chave para o valor que eu quero, ou seja, 'chave'. Meus esforços para obter o valor dessa chave e enviá-la para o log falharam. Alguma outra visão?
Thomas Clowes
75

Este é o meu código para verificar se o json recebido é uma matriz ou dicionário:

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

Eu tentei isso para opções: kNilOptions e NSJSONReadingMutableContainers e funciona corretamente para ambos.

Obviamente, o código real não pode ser assim onde eu crio o ponteiro NSArray ou NSDictionary dentro do bloco if-else.

srik
fonte
29

Funciona para mim. Seu dataobjeto é provavelmente nile, como observou o rckoenes, o objeto raiz deve ser uma matriz (mutável). Veja este código:

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

(Eu tive que escapar das aspas na string JSON com barras invertidas.)

Ole Begemann
fonte
9

Seu código parece bom, exceto que o resultado é um NSArray, não um NSDictionary, aqui está um exemplo:

As duas primeiras linhas apenas criam um objeto de dados com o JSON, o mesmo que você obteria lendo na rede.

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

Conteúdo do NSLog (uma lista de dicionários):

jsonList: (
           {
               id = 1;
               name = Aaa;
           },
           {
               id = 2;
               name = Bbb;
           }
           )
zaph
fonte
O que essa opção (NSJSONReadingMutableContainers) significa. Eu não kNilOption e tudo funciona bem. Diga-me o propósito de usar essas opções
Zar E Ahmer
Maior NSJSONReadingMutableLeavesocorrência no Google:: "Especifica que as seqüências de folhas no gráfico de objeto JSON são criadas como instâncias do NSMutableString."
Zaph 11/11/14
e o que dizer de MutableContainer
Zar E Ahmer
Ops, novamente a partir do resultado principal do Google NSJSONReadingMutableContainers:: "Especifica que matrizes e dicionários são criados como objetos mutáveis".
zaph 11/11
1
Isso ajuda apenas se você planeja modificar o objeto JSON retornado e salvá-lo novamente. Em ambos os casos, os objetos provavelmente são objetos liberados automaticamente e essa parece ser a causa raiz.
Deepak GM
6
[{"id": "1", "name":"Aaa"}, {"id": "2", "name":"Bbb"}]

Nos dados JSON acima, você está mostrando que temos uma matriz que contém o número de dicionários.

Você precisa usar este código para analisá-lo:

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

Para rápido 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"])
    }
kamalesh kumar yadav
fonte
3

O código a seguir busca um objeto JSON de um servidor da web e o analisa em um NSDictionary. Eu usei a API openweathermap que retorna uma resposta JSON simples para este exemplo. Para simplificar, esse código usa solicitações síncronas.

   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);
mahesh chowdary
fonte
Eu acho que sua resposta deve ser a melhor resposta, porque parece a maneira mais rápida de acessar a estrutura JSON.
Porizm
2
As opções não devem usar dois | mas um único | pois eles precisam ser ORed bit a bit.
Deepak GM
A questão não pergunta nada sobre pedidos de rede
Noah Gilmore
2

O @rckoenes já mostrou como obter seus dados corretamente a partir da string JSON.

Para a pergunta que você fez: EXC_BAD_ACCESSquase sempre ocorre quando você tenta acessar um objeto depois que ele é [auto-] liberado. Isso não é específico da serialização JSON [des], mas, ao contrário, apenas tem a ver com a obtenção de um objeto e o acesso a ele após o lançamento. O fato de ter vindo via JSON não importa.

Existem muitas páginas descrevendo como depurar isso - você deseja o Google (ou SO) obj-c zombie objectse, em particular NSZombieEnabled, o que será inestimável para ajudar a determinar a origem dos seus objetos zumbis. ("Zumbi" é o que se chama quando você libera um objeto, mas mantém um ponteiro para ele e tenta fazer referência mais tarde.)

Olie
fonte
1

Swift 2.0 no Xcode 7 (Beta) com bloco 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)
  }
}
Zorayr
fonte
1

NOTA: Para o Swift 3 . Sua string JSON está retornando matriz em vez de dicionário. Por favor, tente o seguinte:

        //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)
        }
Dinesh
fonte
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];
}
deepa
fonte
0

O problema parece ser com a liberação automática de objetos. NSJSONSerialization JSONObjectWithData está obviamente criando alguns objetos liberados automaticamente e passando-os de volta para você. Se você tentar levar isso para um segmento diferente, ele não funcionará, pois não pode ser desalocado em um segmento diferente.

O truque pode ser tentar fazer uma cópia mutável desse dicionário ou matriz e usá-lo.

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

Tratar um NSDictionary como NSArray não resultará em exceção de acesso incorreto, mas provavelmente travará quando uma chamada de método for feita.

Além disso, talvez as opções não sejam realmente importantes aqui, mas é melhor fornecer NSJSONReadingMutableContainers | NSJSONReadingMutableContainers | NSJSONReadingAllowFragments, mas mesmo que sejam objetos liberados automaticamente, ele pode não resolver esse problema.

Deepak GM
fonte
Deepak, você listou NSJSONReadingMutableContainers duas vezes. Você queria que um fosse NSJSONReadingMutableLeaves?
Jk7
0

exemplo ruim, deve ser algo como este {"id": 1, "name": "something as name"}

número e string são misturados.

user1462586
fonte