Jak korzystać z NSJSONSerialization


156

Mam ciąg JSON (z PHP, json_encode()który wygląda tak:

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

Chcę przeanalizować to w jakiejś strukturze danych dla mojej aplikacji na iPhone'a. Myślę, że najlepszą rzeczą dla mnie byłoby posiadanie tablicy słowników, więc zerowym elementem tablicy jest słownik z kluczami "id" => "1"i "name" => "Aaa".

Nie rozumiem jednak, jak NSJSONSerializationprzechowują dane. Oto mój dotychczasowy kod:

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

To jest coś, co widziałem jako przykład na innej stronie internetowej. Próbowałem odczytać JSONobiekt, wypisując liczbę elementów i tym podobne, ale zawsze otrzymuję EXC_BAD_ACCESS.

Jak użyć NSJSONSerializationdo przeanalizowania powyższego JSON i przekształcenia go w strukturę danych, o której wspomniałem?


twoja zmienna danych jest prawdopodobnie nil
d.lebedev

Nie jest, już to przetestowałem.
Logan Serman

Czy próbowałeś sprawdzić, czy w obiekcie błędu są jakieś istotne informacje?
Monolo

Odpowiedzi:


214

Twój główny obiekt json nie jest słownikiem, ale tablicą:

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

To może dać ci jasny obraz tego, jak sobie z tym poradzić:

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

Dzięki, spróbuję, ale nie powinienem niczego [JSON count]zwracać zamiast po prostu dać mi EXC_BAD_ACCESS?
Logan Serman

Powinno dlatego dodałem check czy !jsonArrayi wypisałem błąd. Powinno to spowodować wyświetlenie wszelkich błędów, które wystąpiły podczas analizowania.
rckoenes

1
@ xs2bush nie, ponieważ nie utworzyłeś jsonArraytego pliku, powinien być automatycznie wydany.
rckoenes

@Logan: Tak, [liczba JSON] powinna zwracać wartość. Zobacz moją odpowiedź poniżej dotyczącą zombie. EXC_BAD_ACCESS jest prawie zawsze związane z zombie.
Olie

W tym przypadku element jest kluczem w danej parze klucz-wartość JSON. Twoja pętla for działa doskonale, wyświetlając każdy z moich kluczy JSON. Jednak znam już klucz do wartości, którą chcę, a mianowicie „klucz”. Moje próby uzyskania wartości tego klucza i umieszczenia go w dzienniku zakończyły się niepowodzeniem. Jakieś dalsze spostrzeżenia?
Thomas Clowes

75

To jest mój kod do sprawdzania, czy odebrany plik json jest tablicą lub słownikiem:

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

Wypróbowałem to dla opcji: kNilOptions i NSJSONReadingMutableContainers i działa poprawnie dla obu.

Oczywiście rzeczywisty kod nie może być taki, gdy tworzę wskaźnik NSArray lub NSDictionary w bloku if-else.


29

Mi to pasuje. Twoim dataobiektem jest prawdopodobnie nili, jak zauważył rckoenes, głównym obiektem powinna być (zmienna) tablica. Zobacz ten kod:

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

(Musiałem pominąć cudzysłowy w ciągu JSON z ukośnikami odwrotnymi).


9

Twój kod wydaje się być w porządku, ale wynikiem jest an NSArray, a nie an NSDictionary, oto przykład:

Pierwsze dwa wiersze po prostu tworzą obiekt danych z kodem JSON, tak samo, jak przy odczytywaniu go z sieci.

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

Zawartość NSLog (lista słowników):

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

Co oznacza ta opcja (NSJSONReadingMutableContainers). Nie robię kNilOption i wszystko działa dobrze. Powiedz mi, w jakim celu korzystam z tej opcji
Zar E Ahmer

Największe trafienie w Google:: NSJSONReadingMutableLeaves„Określa, że ​​ciągi liści w grafie obiektów JSON są tworzone jako wystąpienia NSMutableString.”
zaph

a co z MutableContainer
Zar E Ahmer

Ups, znowu z górnego wyniku Google NSJSONReadingMutableContainers:: „Określa, że ​​tablice i słowniki są tworzone jako zmienne obiekty”.
zaph

1
Pomagają one tylko wtedy, gdy planujesz zmodyfikować zwrócony obiekt JSON i zapisać go z powrotem. W obu przypadkach obiekty są prawdopodobnie obiektami udostępnianymi automatycznie i wydaje się, że jest to główna przyczyna.
Deepak GM

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

W powyższych danych JSON pokazujesz, że mamy tablicę zawierającą liczbę słowników.

Aby go przeanalizować, musisz użyć tego kodu:

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

Do szybkiego 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

Poniższy kod pobiera obiekt JSON z serwera WWW i analizuje go do NSDictionary. Użyłem API openweathermap, które zwraca prostą odpowiedź JSON dla tego przykładu. Dla uproszczenia ten kod używa synchronicznych żądań.

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

Myślę, że twoja odpowiedź powinna być najlepszą odpowiedzią, ponieważ wydaje się najszybszym sposobem uzyskania dostępu do struktury JSON.
Porizm,

2
Opcje nie powinny używać dwóch | ale jeden | ponieważ muszą być bitowe ORed.
Deepak GM

Pytanie nie dotyczy zapytań sieciowych
Noah Gilmore,

2

@rckoenes już pokazał, jak poprawnie pobrać dane z ciągu JSON.

Na pytanie, które zadałeś: EXC_BAD_ACCESSprawie zawsze pojawia się, gdy próbujesz uzyskać dostęp do obiektu po tym, jak został on [automatycznie] zwolniony. Nie jest to specyficzne dla [de-] serializacji JSON, ale raczej ma związek z pobraniem obiektu i uzyskaniem do niego dostępu po jego wydaniu. Fakt, że pochodzi z JSON, nie ma znaczenia.

Istnieje wiele stron opisujących, jak to debugować - chcesz Google (lub SO) obj-c zombie objects, aw szczególności NSZombieEnabled, które okażą się nieocenione w określeniu źródła twoich obiektów zombie. (Nazywa się „Zombie”, gdy zwalniasz obiekt, ale trzymasz do niego wskaźnik i próbujesz odwołać się do niego później.)


1

Swift 2.0 na Xcode 7 (Beta) z blokiem 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

UWAGA: dla Swift 3 . Twój ciąg JSON zwraca Array zamiast Dictionary. Wypróbuj następujące rozwiązania:

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

Wydaje się, że problem dotyczy automatycznego zwalniania obiektów. NSJSONSerialization JSONObjectWithData to oczywiście tworzenie niektórych autoreleased obiektów i przekazywanie ich z powrotem. Jeśli spróbujesz przenieść to do innego wątku, nie zadziała, ponieważ nie można go cofnąć w innym wątku.

Sztuczka może polegać na zrobieniu modyfikowalnej kopii tego słownika lub tablicy i użyciu jej.

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

Traktowanie NSDictionary jako NSArray nie spowoduje powstania wyjątku złego dostępu, ale zamiast tego prawdopodobnie spowoduje awarię po wywołaniu metody.

Być może opcje nie mają tutaj znaczenia, ale lepiej jest podać NSJSONReadingMutableContainers | NSJSONReadingMutableContainers | NSJSONReadingAllowFragments, ale nawet jeśli są to obiekty autoreleased, może to nie rozwiązać tego problemu.


Deepak, dwukrotnie wymieniłeś NSJSONReadingMutableContainers. Czy chodziło Ci o to, aby jeden był NSJSONReadingMutableLeaves?
jk7

0

zły przykład, powinno wyglądać mniej więcej tak {"id": 1, "name": "something as name"}

liczba i ciąg są mieszane.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.