Come usare NSJSONSerialization


156

Ho una stringa JSON (da PHP json_encode()che assomiglia a questa:

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

Voglio analizzarlo in una sorta di struttura di dati per la mia app per iPhone. Immagino che la cosa migliore per me sarebbe avere una matrice di dizionari, quindi il nono elemento nella matrice è un dizionario con chiavi "id" => "1"e "name" => "Aaa".

Non capisco come NSJSONSerializationi dati vengano archiviati. Ecco il mio codice finora:

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

Questo è solo qualcosa che ho visto come esempio su un altro sito Web. Ho cercato di ottenere una lettura JSONsull'oggetto stampando il numero di elementi e cose del genere, ma ottengo sempre EXC_BAD_ACCESS.

Come posso utilizzare NSJSONSerializationper analizzare JSON sopra e trasformarlo nella struttura dati che ho citato?


la tua variabile di dati è probabilmente nulla
d.lebedev il

Non lo è, l'ho già provato.
Logan Serman,

Hai provato a vedere se ci sono informazioni rilevanti nell'oggetto errore?
Monolo,

Risposte:


214

Il tuo oggetto root json non è un dizionario ma un array:

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

Questo potrebbe darti un'idea chiara di come gestirlo:

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

Grazie, ci proverò, ma non dovrei [JSON count]restituire qualcosa invece di darmi EXC_BAD_ACCESS?
Logan Serman,

Dovrebbe, è per questo che ho aggiunto il controllo se !jsonArraye stampato l'errore. Questo dovrebbe visualizzare qualsiasi errore che si è verificato durante l'analisi.
rckoenes,

1
@ xs2bush no, poiché non è stato creato jsonArray, dovrebbe essere rilasciato automaticamente.
rckoenes,

@Logan: Sì, [conteggio JSON] dovrebbe restituire un valore. Vedi la mia risposta di seguito riguardo agli zombi. EXC_BAD_ACCESS è quasi sempre legato agli zombi.
Olie,

In questo caso, l'elemento è la chiave in una determinata coppia di valori chiave JSON. Il tuo ciclo for funziona in modo perfetto producendo ciascuna delle mie chiavi JSON. Tuttavia, conosco già la chiave per il valore che desidero, ovvero "chiave". I miei sforzi per ottenere il valore di questa chiave e inviarlo al registro sono falliti. Ulteriori approfondimenti?
Thomas Clowes,

75

Questo è il mio codice per verificare se il json ricevuto è un array o un dizionario:

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

Ho provato questo per le opzioni: kNilOptions e NSJSONReadingMutableContainers e funziona correttamente per entrambi.

Ovviamente, il codice effettivo non può essere in questo modo in cui creo il puntatore NSArray o NSDictionary all'interno del blocco if-else.


29

Per me funziona. Il tuo dataoggetto è probabilmente nile, come notato da rckoenes, l'oggetto root dovrebbe essere un array (mutabile). Vedi questo codice:

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

(Ho dovuto sfuggire alle virgolette nella stringa JSON con barre rovesciate.)


9

Il tuo codice sembra a posto, tranne il risultato è un NSArray, non un NSDictionary, ecco un esempio:

Le prime due righe creano semplicemente un oggetto dati con JSON, lo stesso che si otterrebbe dalla lettura dalla rete.

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

Contenuti NSLog (un elenco di dizionari):

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

Cosa significa questa opzione (NSJSONReadingMutableContainers). Don kNilOption e tutto funziona bene. Dimmi lo scopo di utilizzare queste opzioni
Zar E Ahmer,

Risultato migliore in Google:: NSJSONReadingMutableLeaves"Specifica che le stringhe foglia nel grafico degli oggetti JSON vengono create come istanze di NSMutableString."
zaph,

e che dire di MutableContainer
Zar E Ahmer,

Oops, sempre dal risultato di Google in alto NSJSONReadingMutableContainers:: "Specifica che array e dizionari vengono creati come oggetti mutabili".
zaph,

1
Questi aiutano solo se si prevede di modificare l'oggetto JSON restituito e salvarlo di nuovo. In entrambi i casi, gli oggetti sono probabilmente oggetti rilasciati automaticamente e questa sembra essere la causa principale.
Deepak GM,

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

Nei dati JSON precedenti, stai mostrando che abbiamo un array che contenga il numero di dizionari.

È necessario utilizzare questo codice per analizzarlo:

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

Per rapidi 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

Il codice seguente recupera un oggetto JSON da un server Web e lo analizza in un NSDictionary. Ho usato l'API openweathermap che restituisce una semplice risposta JSON per questo esempio. Per semplicità, questo codice utilizza richieste sincrone.

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

Penso che la tua risposta dovrebbe essere la migliore risposta perché sembra il modo più veloce per accedere alla struttura JSON.
Porizm,

2
Le opzioni non dovrebbero usare due | ma un singolo | poiché devono essere OR bit per bit.
Deepak GM,

La domanda non fa nulla sulle richieste della rete
Noah Gilmore,

2

@rckoenes ti ha già mostrato come ottenere correttamente i tuoi dati dalla stringa JSON.

Alla domanda che hai posto: EXC_BAD_ACCESSarriva quasi sempre quando provi ad accedere a un oggetto dopo che è stato rilasciato [auto-]. Questo non è specifico per la [de-] serializzazione JSON ma, piuttosto, ha a che fare con te per ottenere un oggetto e accedervi dopo che è stato rilasciato. Il fatto che sia arrivato tramite JSON non ha importanza.

Ci sono molte pagine che descrivono come eseguire il debug di questo: vuoi Google (o SO) obj-c zombie objectse, in particolare NSZombieEnabled, che si riveleranno preziose per te nel determinare la fonte dei tuoi oggetti zombi. ("Zombie" è quello che viene chiamato quando rilasci un oggetto ma tieni un puntatore su di esso e prova a fare riferimento in un secondo momento.)


1

Swift 2.0 su Xcode 7 (Beta) con blocco 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

NOTA: per Swift 3 . La stringa JSON restituisce array anziché Dizionario. Si prega di provare quanto segue:

        //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

Il problema sembra riguardare il rilascio automatico di oggetti. NSJSONSerialization JSONObjectWithData sta ovviamente creando alcuni oggetti rilasciati automaticamente e te li restituisce. Se provi a portarlo su un thread diverso, non funzionerà poiché non può essere deallocato su un thread diverso.

Il trucco potrebbe essere quello di provare a fare una copia mutabile di quel dizionario o array e usarlo.

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

Trattare un NSDictionary come NSArray non comporterà un'eccezione di accesso errato ma probabilmente si arresterà in modo anomalo quando viene effettuata una chiamata al metodo.

Inoltre, le opzioni potrebbero non essere importanti qui, ma è meglio fornire NSJSONReadingMutableContainers | NSJSONReadingMutableContainers | NSJSONReadingAllowFragments ma anche se si tratta di oggetti rilasciati automaticamente, il problema potrebbe non essere risolto.


Deepak, hai elencato due volte NSJSONReadingMutableContainers. Volevi dire che uno sarebbe NSJSONReadingMutableLeaves?
jk7

0

cattivo esempio, dovrebbe essere qualcosa del genere {"id": 1, "name": "qualcosa come nome"}

numero e stringa sono mescolati.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.