Comment utiliser NSJSONSerialization

156

J'ai une chaîne JSON (de PHP json_encode()qui ressemble à ceci:

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

Je veux analyser cela dans une sorte de structure de données pour mon application iPhone. Je suppose que la meilleure chose pour moi serait d'avoir un tableau de dictionnaires, donc le 0ème élément du tableau est un dictionnaire avec les clés "id" => "1"et "name" => "Aaa".

NSJSONSerializationCependant, je ne comprends pas comment le stocke les données. Voici mon code pour l'instant:

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

C'est juste quelque chose que j'ai vu comme exemple sur un autre site Web. J'ai essayé de lire l' JSONobjet en imprimant le nombre d'éléments et de choses comme ça, mais je reçois toujours EXC_BAD_ACCESS.

Comment puis-je utiliser NSJSONSerializationpour analyser le JSON ci-dessus et le transformer en la structure de données que j'ai mentionnée?

Logan Serman
la source
votre variable de données est probablement nulle
d.lebedev
Ce n'est pas le cas, je l'ai déjà testé.
Logan Serman
Avez-vous essayé de voir s'il y avait des informations pertinentes dans l'objet d'erreur?
Monolo

Réponses:

214

Votre objet json racine n'est pas un dictionnaire mais un tableau:

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

Cela pourrait vous donner une idée claire de la façon de le gérer:

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
la source
Merci, je vais essayer, mais je ne devrais pas [JSON count]retourner quelque chose au lieu de me donner simplement EXC_BAD_ACCESS?
Logan Serman
Il devrait, c'est pourquoi j'ai ajouté le chèque si !jsonArrayet imprimé l'erreur. Cela devrait afficher toute erreur survenue lors de l'analyse.
rckoenes
1
@ xs2bush non, puisque vous n'avez pas créé le, jsonArrayil devrait être autorelease.
rckoenes
@Logan: Oui, [JSON count] doit renvoyer une valeur. Voir ma réponse ci-dessous concernant les zombies. EXC_BAD_ACCESS est presque toujours lié aux zombies.
Olie
Dans ce cas, l'élément est la clé dans une paire clé valeur JSON donnée .. votre boucle for fonctionne parfaitement en sortie chacune de mes clés JSON. Cependant, je connais déjà la clé de la valeur que je veux, à savoir «clé». Mes efforts pour obtenir la valeur de cette clé et l'afficher dans le journal ont échoué. Un autre aperçu?
Thomas Clowes
75

C'est mon code pour vérifier si le json reçu est un tableau ou un dictionnaire:

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

J'ai essayé ceci pour les options: kNilOptions et NSJSONReadingMutableContainers et fonctionne correctement pour les deux.

De toute évidence, le code réel ne peut pas être de cette façon où je crée le pointeur NSArray ou NSDictionary dans le bloc if-else.

srik
la source
29

Ça marche pour moi. Votre dataobjet est probablement nilet, comme l'a noté rckoenes, l'objet racine devrait être un tableau (mutable). Voir ce code:

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

(J'ai dû échapper aux guillemets dans la chaîne JSON avec des barres obliques inverses.)

Ole Begemann
la source
9

Votre code semble correct sauf que le résultat est un NSArray, pas un NSDictionary, voici un exemple:

Les deux premières lignes créent simplement un objet de données avec le JSON, comme vous le feriez en le lisant sur le net.

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

Contenu NSLog (une liste de dictionnaires):

jsonList: (
           {
               id = 1;
               name = Aaa;
           },
           {
               id = 2;
               name = Bbb;
           }
           )
zaph
la source
Ce que signifie cette option (NSJSONReadingMutableContainers). Je ne kNilOption et tout fonctionne bien. Dites-moi le but de l'utilisation de ces options
Zar E Ahmer
Top hit dans Google: NSJSONReadingMutableLeaves"Spécifie que les chaînes feuille dans le graphique d'objets JSON sont créées en tant qu'instances de NSMutableString."
zaph
et que dire de MutableContainer
Zar E Ahmer
Oups, encore une fois à partir du résultat supérieur de Google NSJSONReadingMutableContainers: "Spécifie que les tableaux et les dictionnaires sont créés en tant qu'objets mutables."
zaph
1
Celles-ci ne sont utiles que si vous prévoyez de modifier l'objet JSON renvoyé et de le réenregistrer. Dans les deux cas, les objets sont probablement des objets libérés automatiquement et cela semble être la cause première.
Deepak GM
6
[{"id": "1", "name":"Aaa"}, {"id": "2", "name":"Bbb"}]

Dans les données JSON ci-dessus, vous montrez que nous avons un tableau contenant le nombre de dictionnaires.

Vous devez utiliser ce code pour l'analyser:

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

Pour 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"])
    }
kamalesh kumar yadav
la source
3

Le code suivant récupère un objet JSON à partir d'un serveur Web et l'analyse en NSDictionary. J'ai utilisé l'API openweathermap qui renvoie une réponse JSON simple pour cet exemple. Pour rester simple, ce code utilise des requêtes synchrones.

   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
la source
Je pense que votre réponse devrait être la meilleure réponse car cela semble le moyen le plus rapide d'accéder à la structure JSON.
Porizm
2
Les options ne doivent pas utiliser deux | mais un seul | car ils doivent être OU au niveau du bit.
Deepak GM
La question ne pose rien sur les demandes réseau
Noah Gilmore
2

@rckoenes vous a déjà montré comment obtenir correctement vos données à partir de la chaîne JSON.

À la question que vous avez posée: EXC_BAD_ACCESSvient presque toujours lorsque vous essayez d'accéder à un objet après qu'il a été [auto-] libéré. Ce n'est pas spécifique à la [dé-] sérialisation JSON mais, plutôt, cela a juste à voir avec vous obtenir un objet et y accéder ensuite après sa publication. Le fait qu'il soit venu via JSON n'a pas d'importance.

Il existe de nombreuses pages décrivant comment déboguer cela - vous voulez Google (ou SO) obj-c zombie objectset, en particulier NSZombieEnabled, qui se révélera inestimable pour vous aider à déterminer la source de vos objets zombies. ("Zombie" est ce qu'il appelle lorsque vous relâchez un objet mais que vous gardez un pointeur dessus et que vous essayez de le référencer plus tard.)

Olie
la source
1

Swift 2.0 sur Xcode 7 (Beta) avec bloc 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
la source
1

REMARQUE: pour Swift 3 . Votre chaîne JSON renvoie Array au lieu de Dictionary. Veuillez essayer ce qui suit:

        //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
la source
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
la source
0

Le problème semble être lié à la libération automatique des objets. NSJSONSerialization JSONObjectWithData crée évidemment des objets libérés automatiquement et vous les renvoie. Si vous essayez de transférer cela sur un thread différent, cela ne fonctionnera pas car il ne peut pas être désalloué sur un thread différent.

L'astuce pourrait être d'essayer de faire une copie modifiable de ce dictionnaire ou de ce tableau et de l'utiliser.

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

Traiter un NSDictionary comme NSArray n'entraînera pas d'exception d'accès incorrect, mais au lieu de cela, il plantera probablement lorsqu'un appel de méthode est effectué.

De plus, les options n'ont peut-être pas vraiment d'importance ici, mais il est préférable de donner NSJSONReadingMutableContainers | NSJSONReadingMutableContainers | NSJSONReadingAllowFragments, mais même s'il s'agit d'objets libérés automatiquement, il se peut que ce problème ne soit pas résolu.

Deepak GM
la source
Deepak, vous avez répertorié NSJSONReadingMutableContainers deux fois. Vouliez-vous dire que l'un d'eux était NSJSONReadingMutableLeaves?
jk7
0

mauvais exemple, devrait être quelque chose comme ceci {"id": 1, "name": "something as name"}

nombre et chaîne sont mélangés.

user1462586
la source