Cómo usar la serialización NSJSONS

156

Tengo una cadena JSON (de PHP json_encode()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 matriz de diccionarios, por lo que el elemento 0 en la matriz es un diccionario con teclas "id" => "1"y "name" => "Aaa".

Sin NSJSONSerializationembargo , no entiendo cómo almacena los datos. Aquí está mi código hasta ahora:

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

Esto es algo que vi como ejemplo en otro sitio web. He estado intentando obtener una lectura del JSONobjeto imprimiendo la cantidad de elementos y cosas así, pero siempre estoy obteniendo EXC_BAD_ACCESS.

¿Cómo uso NSJSONSerializationpara analizar el JSON anterior y convertirlo en la estructura de datos que mencioné?

Logan Serman
fuente
su datos variable es probablemente nula
d.lebedev
No lo es, ya lo he probado.
Logan Serman
¿Has intentado ver si hay información relevante en el objeto de error?
Monolo

Respuestas:

214

Su objeto raíz 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);
   }
}
rckoenes
fuente
Gracias. Intentaré eso, pero ¿no debería [JSON count]devolver algo en lugar de simplemente darme EXC_BAD_ACCESS?
Logan Serman
Debería, por eso agregué el cheque si !jsonArraye imprimí el error. Esto debería mostrar cualquier error que ocurriera durante el análisis.
rckoenes
1
@ xs2bush no, ya que no jsonArraylo creaste, debería ser de liberación automática.
rckoenes
@Logan: Sí, [recuento JSON] debería devolver un valor. Vea mi respuesta a continuación con respecto a los zombies. EXC_BAD_ACCESS casi siempre está relacionado con zombies.
Olie
En este caso, el elemento es la clave en un par de valores de clave JSON dado ... su bucle for funciona perfectamente con la salida de cada una de mis claves JSON. Sin embargo, ya conozco la clave para el valor que quiero, a saber, 'clave'. Mis esfuerzos por obtener el valor de esta clave y enviarlo al registro han fallado. ¿Alguna idea más?
Thomas Clowes
75

Este es mi código para verificar si el json recibido es una matriz o 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 para 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.

srik
fuente
29

Esto funciona para mi. Su dataobjeto es probablemente nily, como señaló rckoenes, el objeto raíz debería ser una matriz (mutable). Ver 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 invertidas).

Ole Begemann
fuente
9

Su código parece estar bien, excepto que el resultado es un NSArray, no unNSDictionary , aquí hay un ejemplo:

Las dos primeras líneas solo crean un objeto de datos con el JSON, lo mismo que obtendría al leerlo desde 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 NSLog (una lista de diccionarios):

jsonList: (
           {
               id = 1;
               name = Aaa;
           },
           {
               id = 2;
               name = Bbb;
           }
           )
zaph
fuente
Qué significa esta opción (NSJSONReadingMutableContainers). No uso kNilOption y todo funciona bien. Dime el propósito de usar estas opciones
Zar E Ahmer
Top hit en Google:: NSJSONReadingMutableLeaves"Especifica que las cadenas de hojas en el gráfico de objetos JSON se crean como instancias de NSMutableString".
zaph
y qué hay de MutableContainer
Zar E Ahmer
Vaya, de nuevo desde el resultado superior de Google NSJSONReadingMutableContainers:: "Especifica que las matrices y los diccionarios se crean como objetos mutables".
zaph
1
Esto solo ayuda si planea modificar el objeto JSON devuelto y guardarlo de nuevo. En cualquier caso, los objetos son probablemente objetos auto liberados y esa parece ser la causa raíz.
Deepak GM
6
[{"id": "1", "name":"Aaa"}, {"id": "2", "name":"Bbb"}]

En los datos JSON anteriores, está mostrando que tenemos una matriz que contiene el número de diccionarios.

Debe 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 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
fuente
3

El siguiente código obtiene 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);
mahesh chowdary
fuente
Creo que su respuesta debería ser la mejor respuesta porque parece la forma más rápida de acceder a la estructura JSON.
Porizm
2
Las opciones no deben usar dos | pero un solo | ya que necesitan ser ORed bit a bit.
Deepak GM
La pregunta no hace nada sobre las solicitudes de red
Noah Gilmore
2

@rckoenes ya le mostró cómo obtener correctamente sus datos de la cadena JSON.

A la pregunta que hizo: EXC_BAD_ACCESScasi siempre aparece cuando intenta acceder a un objeto después de que se ha lanzado [auto-]. Esto no es específico de la [des] serialización JSON, sino que solo tiene que ver con que usted obtenga un objeto y luego acceda a él después de que se haya lanzado. El hecho de que vino a través de JSON no importa.

Hay muchísimas páginas que describen cómo depurar esto: desea Google (o SO) obj-c zombie objectsy, en particular NSZombieEnabled, lo que le resultará invaluable para ayudarlo a determinar la fuente de sus objetos zombie. ("Zombi" es como se llama cuando sueltas un objeto pero mantienes un puntero sobre él e intentas hacer referencia a él más adelante)

Olie
fuente
1

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)
  }
}
Zorayr
fuente
1

NOTA: Para Swift 3 . Su cadena JSON está devolviendo Array en lugar de Dictionary. Por favor 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)
        }
Dinesh
fuente
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
fuente
0

El problema parece ser con la liberación automática de objetos. NSJSONSerialization JSONObjectWithData obviamente está creando algunos objetos lanzados automáticamente y se los está devolviendo. Si intenta llevar eso 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 probablemente se bloqueará cuando se realice una llamada al 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 de autorremisión, puede que no resuelva este problema.

Deepak GM
fuente
Deepak, usted enumeró NSJSONReadingMutableContainers dos veces. ¿Querías decir que uno es NSJSONReadingMutableLeaves?
jk7
0

mal ejemplo, debería ser algo como esto {"id": 1, "name": "algo como nombre"}

número y cadena se mezclan.

usuario1462586
fuente