Verwendung von NSJSONSerialization


156

Ich habe eine JSON-Zeichenfolge (von PHPs, json_encode()die so aussieht:

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

Ich möchte dies in eine Art Datenstruktur für meine iPhone-App analysieren. Ich denke, das Beste für mich wäre, ein Array von Wörterbüchern zu haben, also ist das 0. Element im Array ein Wörterbuch mit Schlüsseln "id" => "1"und "name" => "Aaa".

Ich verstehe allerdings nicht, wie NSJSONSerializationdie Daten gespeichert werden. Hier ist mein Code bisher:

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

Dies ist nur ein Beispiel, das ich auf einer anderen Website als Beispiel gesehen habe. Ich habe versucht, das JSONObjekt zu lesen, indem ich die Anzahl der Elemente und ähnliches ausgedruckt habe, aber ich bekomme immer etwas EXC_BAD_ACCESS.

Wie NSJSONSerializationanalysiere ich den oben genannten JSON und wandle ihn in die von mir erwähnte Datenstruktur um?


Ihre Daten variabel ist wahrscheinlich null
d.lebedev

Es ist nicht so, das habe ich schon getestet.
Logan Serman

Haben Sie versucht festzustellen, ob das Fehlerobjekt relevante Informationen enthält?
Monolo

Antworten:


214

Ihr Root-JSON-Objekt ist kein Wörterbuch, sondern ein Array:

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

Dies könnte Ihnen ein klares Bild davon geben, wie Sie damit umgehen sollen:

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

Danke, ich werde das versuchen, sollte aber nichts [JSON count]zurückgeben, anstatt mir nur EXC_BAD_ACCESS zu geben?
Logan Serman

Deshalb sollte ich die Prüfung hinzufügen !jsonArrayund den Fehler ausdrucken. Dies sollte alle Fehler anzeigen, die während des Parsens aufgetreten sind.
rckoenes

1
@ xs2bush nein, da du das nicht erstellt hast jsonArraysollte es autorelease sein.
rckoenes

@Logan: Ja, [JSON-Anzahl] sollte einen Wert zurückgeben. Siehe meine Antwort unten in Bezug auf Zombies. EXC_BAD_ACCESS hat fast immer mit Zombies zu tun.
Olie

In diesem Fall ist item der Schlüssel in einem bestimmten JSON-Schlüsselwertpaar. Ihre for-Schleife funktioniert perfekt, wenn jeder meiner JSON-Schlüssel ausgegeben wird. Ich kenne jedoch bereits den Schlüssel für den gewünschten Wert, nämlich 'Schlüssel'. Meine Bemühungen, den Wert dieses Schlüssels abzurufen und in das Protokoll auszugeben, sind fehlgeschlagen. Weitere Einblicke?
Thomas Clowes

75

Dies ist mein Code, um zu überprüfen, ob der empfangene JSON ein Array oder ein Wörterbuch ist:

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

Ich habe dies für Optionen versucht: kNilOptions und NSJSONReadingMutableContainers und funktioniert für beide korrekt.

Offensichtlich kann der eigentliche Code nicht so sein, wie ich den NSArray- oder NSDictionary-Zeiger innerhalb des if-else-Blocks erstelle.


29

Für mich geht das. Ihr dataObjekt ist wahrscheinlich nilund, wie rckoenes bemerkt hat, sollte das Stammobjekt ein (veränderliches) Array sein. Siehe diesen 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);

(Ich musste die Anführungszeichen in der JSON-Zeichenfolge mit Backslashes umgehen.)


9

Ihr Code scheint in Ordnung zu sein, außer dass das Ergebnis ein NSArrayund kein NSDictionaryist. Hier ein Beispiel:

In den ersten beiden Zeilen wird nur ein Datenobjekt mit dem JSON erstellt, so wie Sie es aus dem Netz lesen würden.

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

NSLog-Inhalte (eine Liste von Wörterbüchern):

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

Was diese Option (NSJSONReadingMutableContainers) bedeutet. Ich don kNilOption und alles funktioniert gut. Sagen Sie mir den Zweck der Verwendung dieser Option
Zar E Ahmer

Top-Hit in Google NSJSONReadingMutableLeaves:: "Gibt an, dass Blattzeichenfolgen im JSON-Objektdiagramm als Instanzen von NSMutableString erstellt werden."
Zaph

und was ist mit MutableContainer
Zar E Ahmer

Hoppla, noch einmal aus dem Top-Google-Ergebnis: NSJSONReadingMutableContainers"Gibt an, dass Arrays und Wörterbücher als veränderbare Objekte erstellt werden."
Zaph

1
Diese helfen nur, wenn Sie das zurückgegebene JSON-Objekt ändern und zurückspeichern möchten. In beiden Fällen handelt es sich bei den Objekten wahrscheinlich um automatisch freigegebene Objekte, und dies scheint die Hauptursache zu sein.
Deepak GM

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

In den obigen JSON-Daten zeigen Sie, dass wir ein Array haben, das die Anzahl der Wörterbücher enthält.

Sie müssen diesen Code zum Parsen verwenden:

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

Für schnelles 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

Der folgende Code ruft ein JSON-Objekt von einem Webserver ab und analysiert es in ein NSDictionary. Ich habe die openweathermap-API verwendet, die für dieses Beispiel eine einfache JSON-Antwort zurückgibt. Zur Vereinfachung verwendet dieser Code synchrone Anforderungen.

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

Ich denke, Ihre Antwort sollte die beste Antwort sein, da dies der schnellste Weg ist, auf die JSON-Struktur zuzugreifen.
Porizm

2
Die Optionen sollten nicht zwei | verwenden aber ein einziger | da sie bitweise ODER-verknüpft werden müssen.
Deepak GM

Die Frage stellt nichts über Netzwerkanfragen
Noah Gilmore

2

@rckoenes hat Ihnen bereits gezeigt, wie Sie Ihre Daten korrekt aus der JSON-Zeichenfolge abrufen können.

Auf die Frage, die Sie gestellt haben: EXC_BAD_ACCESSKommt fast immer, wenn Sie versuchen, auf ein Objekt zuzugreifen, nachdem es [automatisch] freigegeben wurde. Dies ist nicht spezifisch für die JSON [De-] Serialisierung, sondern hat nur damit zu tun, dass Sie ein Objekt abrufen und dann darauf zugreifen, nachdem es freigegeben wurde. Die Tatsache, dass es über JSON kam, spielt keine Rolle.

Es gibt viele, viele Seiten, auf denen beschrieben wird, wie Sie dies debuggen können - Sie möchten Google (oder SO) obj-c zombie objectsund insbesondere NSZombieEnabled, was für Sie von unschätzbarem Wert ist, um die Quelle Ihrer Zombie-Objekte zu bestimmen. ("Zombie" heißt es, wenn Sie ein Objekt freigeben, aber einen Zeiger darauf behalten und später versuchen, darauf zu verweisen.)


1

Swift 2.0 auf Xcode 7 (Beta) mit do / try / catch-Block:

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

HINWEIS: Für Swift 3 . Ihr JSON-String gibt Array anstelle von Dictionary zurück. Bitte probieren Sie Folgendes aus:

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

Das Problem scheint bei der automatischen Freigabe von Objekten zu liegen. NSJSONSerialization JSONObjectWithData erstellt offensichtlich einige automatisch freigegebene Objekte und gibt sie an Sie zurück. Wenn Sie versuchen, dies auf einen anderen Thread zu übertragen, funktioniert dies nicht, da die Zuordnung zu einem anderen Thread nicht aufgehoben werden kann.

Der Trick könnte darin bestehen, eine veränderbare Kopie dieses Wörterbuchs oder Arrays zu erstellen und es zu verwenden.

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

Die Behandlung eines NSDictionary als NSArray führt nicht zu einer Ausnahme für einen fehlerhaften Zugriff, sondern stürzt wahrscheinlich ab, wenn ein Methodenaufruf ausgeführt wird.

Möglicherweise sind die Optionen hier auch nicht wirklich wichtig, aber es ist besser, NSJSONReadingMutableContainers | anzugeben NSJSONReadingMutableContainers | NSJSONReadingAllowFragments, aber selbst wenn es sich um automatisch freigegebene Objekte handelt, kann dieses Problem möglicherweise nicht behoben werden.


Deepak, Sie haben NSJSONReadingMutableContainers zweimal aufgelistet. Meinten Sie, dass einer NSJSONReadingMutableLeaves sein sollte?
jk7

0

schlechtes Beispiel sollte so etwas sein {"id": 1, "name": "etwas als Name"}

Nummer und Zeichenfolge werden gemischt.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.