ios - Cómo usar NSJSONSerialization
objective-c nsdictionary (11)
@rckoenes ya le mostró cómo obtener correctamente sus datos de la cadena JSON.
A la pregunta que hizo: EXC_BAD_ACCESS
casi siempre aparece cuando intenta acceder a un objeto después de que ha sido [auto-] lanzado. Esto no es específico de la [des] serialización de JSON, sino que tiene que ver con que consigas un objeto y luego accedas a él después de que se haya lanzado. El hecho de que haya venido a través de JSON no importa.
Hay muchas páginas que describen cómo depurar esto. Desea NSZombieEnabled
obj-c zombie objects
Google (o SO) y, en particular, NSZombieEnabled
, lo cual será de gran valor para ayudar a determinar la fuente de sus objetos zombis. ("Zombie" es como se llama cuando liberas un objeto, pero mantén un puntero e intenta hacer referencia más adelante).
Tengo una cadena JSON (del json_encode()
de PHP que se ve así:
[{"id": "1", "name":"Aaa"}, {"id": "2", "name":"Bbb"}]
Quiero analizar esto en algún tipo de estructura de datos para mi aplicación de iPhone. Supongo que lo mejor para mí sería tener una variedad de diccionarios, por lo que el 0º elemento de la matriz es un diccionario con las claves "id" => "1"
y "name" => "Aaa"
.
No NSJSONSerialization
no entiendo cómo almacena NSJSONSerialization
los datos. Aquí está mi código hasta ahora:
NSError *e = nil;
NSDictionary *JSON = [NSJSONSerialization
JSONObjectWithData: data
options: NSJSONReadingMutableContainers
error: &e];
Esto es solo algo que vi como un ejemplo en otro sitio web. He intentado leer el objeto JSON
al imprimir la cantidad de elementos y cosas así, pero siempre EXC_BAD_ACCESS
.
¿Cómo uso NSJSONSerialization
para analizar el JSON anterior y convertirlo en la estructura de datos que mencioné?
El problema parece ser con la liberación automática de objetos. NSJSONSerialization JSONObjectWithData obviamente está creando algunos objetos liberados automáticamente y se los está devolviendo. Si intenta llevarlo a un hilo diferente, no funcionará, ya que no se puede desasignar en un hilo diferente.
El truco podría ser intentar hacer una copia mutable de ese diccionario o matriz y usarlo.
NSError *e = nil;
id jsonObject = [NSJSONSerialization
JSONObjectWithData: data
options: NSJSONReadingMutableContainers
error: &e] mutableCopy];
Tratar un NSDictionary como NSArray no dará como resultado una excepción de acceso incorrecto, sino que se bloqueará cuando se realice una llamada a un método.
Además, puede ser que las opciones realmente no importen aquí, pero es mejor dar NSJSONReadingMutableContainers | NSJSONReadingMutableContainers | NSJSONReadingAllowFragments pero incluso si son objetos liberados automáticamente, es posible que no resuelva este problema.
El siguiente código recupera un objeto JSON de un servidor web y lo analiza en un NSDictionary. He usado la API openweathermap que devuelve una respuesta JSON simple para este ejemplo. Para mantenerlo simple, este código usa solicitudes sincrónicas.
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);
Este es mi código para verificar si el json recibido es una matriz o un diccionario:
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);
}
He intentado esto con las opciones: kNilOptions y NSJSONReadingMutableContainers y funciona correctamente para ambos.
Obviamente, el código real no puede ser de esta manera donde creo el puntero NSArray o NSDictionary dentro del bloque if-else.
Esto funciona para mi. Su objeto de data
es probablemente nil
y, como notó Rckoenes, el objeto raíz debe ser una matriz (mutable). Vea 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);
(Tuve que escapar de las comillas en la cadena JSON con barras diagonales inversas.)
Su código parece correcto, excepto que el resultado es un NSArray
, no un NSDictionary
, aquí hay un ejemplo:
Las primeras dos líneas solo crean un objeto de datos con el JSON, el mismo que obtendría al leerlo de la red.
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);
Contenido de NSLog (una lista de diccionarios):
jsonList: (
{
id = 1;
name = Aaa;
},
{
id = 2;
name = Bbb;
}
)
Su objeto root json no es un diccionario sino una matriz:
[{"id": "1", "name":"Aaa"}, {"id": "2", "name":"Bbb"}]
Esto podría darle una idea clara de cómo manejarlo:
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);
}
}
NOTA: para Swift 3 . Su cadena JSON devuelve matriz en lugar de diccionario. Pruebe lo siguiente:
//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)
}
Swift 2.0 en Xcode 7 (Beta) con bloque 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)
}
}
#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];
}
[{"id": "1", "name":"Aaa"}, {"id": "2", "name":"Bbb"}]
En los datos JSON anteriores, está mostrando que tenemos una matriz que contiene la cantidad de diccionarios.
Necesita usar este código para analizarlo:
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 swift 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"])
}