Cara menggunakan NSJSONSerialization

156

Saya memiliki string JSON (dari PHP json_encode()yang terlihat seperti ini:

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

Saya ingin menguraikan ini menjadi semacam struktur data untuk aplikasi iPhone saya. Saya kira hal terbaik bagi saya adalah memiliki array kamus, jadi elemen 0 dalam array adalah kamus dengan kunci "id" => "1"dan "name" => "Aaa".

Saya tidak mengerti bagaimana NSJSONSerializationmenyimpan data. Ini kode saya sejauh ini:

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

Ini adalah sesuatu yang saya lihat sebagai contoh di situs web lain. Saya telah mencoba membaca JSONobjek dengan mencetak sejumlah elemen dan hal-hal seperti itu, tetapi saya selalu mengerti EXC_BAD_ACCESS.

Bagaimana saya menggunakan NSJSONSerializationuntuk mem-parse JSON di atas, dan mengubahnya menjadi struktur data yang saya sebutkan?

Logan Serman
sumber
variabel data Anda mungkin nihil
d.lebedev
Bukan, saya sudah mengujinya.
Logan Serman
Sudahkah Anda mencoba melihat apakah ada informasi yang relevan di objek kesalahan?
Monolo

Jawaban:

214

Objek json root Anda bukan kamus tetapi array:

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

Ini mungkin memberi Anda gambaran yang jelas tentang cara menanganinya:

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);
   }
}
rckoene
sumber
Terima kasih, saya akan mencobanya, tetapi tidak boleh [JSON count]mengembalikan sesuatu, bukan hanya memberi saya EXC_BAD_ACCESS?
Logan Serman
Seharusnya, itu sebabnya saya menambahkan cek jika !jsonArraydan mencetak kesalahan. Ini akan menampilkan kesalahan apa pun yang terjadi selama penguraian.
rckoenes
1
@ xs2bush tidak, karena Anda tidak membuat jsonArrayitu harus autorelease.
rckoenes
@ Logo: Ya, [JSON count] harus mengembalikan nilai. Lihat jawaban saya di bawah ini tentang zombie. EXC_BAD_ACCESS hampir selalu berhubungan dengan zombie.
Olie
Dalam hal ini, item adalah kunci dalam pasangan nilai kunci JSON yang diberikan .. for for Anda bekerja dengan sempurna mengeluarkan setiap kunci JSON saya. Namun saya sudah tahu kunci untuk nilai yang saya inginkan, yaitu 'kunci'. Upaya saya untuk mendapatkan nilai kunci ini dan mengeluarkannya ke log telah gagal. Adakah wawasan lebih lanjut?
Thomas Clowes
75

Ini adalah kode saya untuk memeriksa apakah json yang diterima adalah array atau kamus:

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

Saya telah mencoba ini untuk opsi: kNilOptions dan NSJSONReadingMutableContainers dan berfungsi dengan benar untuk keduanya.

Jelas, kode aktual tidak bisa seperti ini di mana saya membuat pointer NSArray atau NSDictionary di dalam blok if-else.

srik
sumber
29

Ini bekerja untuk saya. dataObjek Anda mungkin nildan, seperti yang dicatat rckoenes, objek root harus berupa array (bisa berubah). Lihat kode ini:

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

(Saya harus melarikan diri dari kutipan dalam string JSON dengan garis miring terbalik.)

Ole Begemann
sumber
9

Kode Anda tampak baik-baik saja kecuali hasilnya adalah NSArray, bukan NSDictionary, di sini adalah contoh:

Dua baris pertama hanya membuat objek data dengan JSON, sama seperti Anda akan membacanya dari internet.

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

Konten NSLog (daftar kamus):

jsonList: (
           {
               id = 1;
               name = Aaa;
           },
           {
               id = 2;
               name = Bbb;
           }
           )
zaph
sumber
Apa arti opsi ini (NSJSONReadingMutableContainers) artinya. Saya don kNilOption dan semuanya berfungsi dengan baik. Katakan tujuan penggunaan opsi ini
Zar E Ahmer
Hit teratas di Google:: NSJSONReadingMutableLeaves"Menentukan bahwa string daun dalam grafik objek JSON dibuat sebagai instance dari NSMutableString."
zaph
dan bagaimana dengan MutableContainer
Zar E Ahmer
Ups, lagi dari hasil Google teratas NSJSONReadingMutableContainers:: "Menentukan bahwa array dan kamus dibuat sebagai objek yang dapat diubah."
zaph
1
Ini hanya membantu jika Anda berencana untuk memodifikasi objek JSON yang dikembalikan dan menyimpannya kembali. Dalam kedua kasus, objek mungkin objek autoreleased dan yang tampaknya menjadi akar penyebabnya.
Deepak GM
6
[{"id": "1", "name":"Aaa"}, {"id": "2", "name":"Bbb"}]

Di data JSON di atas, Anda menunjukkan bahwa kami memiliki larik yang berisi jumlah kamus.

Anda perlu menggunakan kode ini untuk menguraikannya:

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

Untuk 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
sumber
3

Kode berikut mengambil objek JSON dari server web, dan mem-parsingnya ke NSDictionary. Saya telah menggunakan API openweathermap yang mengembalikan respons JSON sederhana untuk contoh ini. Agar sederhana, kode ini menggunakan permintaan sinkron.

   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
sumber
Saya pikir jawaban Anda harus menjadi jawaban terbaik karena sepertinya cara tercepat untuk mengakses struktur JSON.
Porizm
2
Opsi tidak boleh menggunakan dua | tapi satu | karena mereka perlu bitwise ORed.
Deepak GM
Pertanyaannya tidak menanyakan apa pun tentang permintaan jaringan
Noah Gilmore
2

@ Arckoenes sudah menunjukkan kepada Anda cara mendapatkan data dengan benar dari string JSON.

Untuk pertanyaan yang Anda ajukan: EXC_BAD_ACCESShampir selalu muncul ketika Anda mencoba mengakses objek setelah [otomatis] dirilis. Ini tidak khusus untuk serialisasi JSON [de-] tetapi, lebih tepatnya, hanya berkaitan dengan Anda mendapatkan objek dan kemudian mengaksesnya setelah dirilis. Fakta bahwa itu datang melalui JSON tidak masalah.

Ada banyak-banyak halaman yang menjelaskan cara men-debug ini - Anda ingin Google (atau SO) obj-c zombie objectsdan, khususnya NSZombieEnabled,, yang akan terbukti sangat berharga bagi Anda dalam membantu menentukan sumber objek zombie Anda. ("Zombie" adalah sebutannya ketika Anda melepaskan objek tetapi menyimpan pointer ke sana dan mencoba untuk referensi nanti.)

Olie
sumber
1

Swift 2.0 pada Xcode 7 (Beta) dengan blok 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
sumber
1

CATATAN: Untuk Swift 3 . String JSON Anda mengembalikan Array alih-alih Kamus. Silakan coba yang berikut ini:

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

Masalahnya tampaknya dengan autorelease objek. NSJSONSerialization JSONObjectWithData jelas membuat beberapa objek autoreleased dan meneruskannya kembali kepada Anda. Jika Anda mencoba untuk mengaktifkannya pada utas yang berbeda, itu tidak akan berhasil karena tidak dapat dibatalkan alokasi pada utas yang berbeda.

Triknya adalah mencoba melakukan salinan kamus atau larik yang dapat diubah dan menggunakannya.

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

Memperlakukan NSDictionary sebagai NSArray tidak akan menghasilkan pengecualian akses yang buruk tetapi malah akan crash ketika pemanggilan metode dilakukan.

Juga, mungkin opsi tidak terlalu penting di sini tetapi lebih baik untuk memberikan NSJSONReadingMutableContainers | NSJSONReadingMutableContainers | NSJSONReadingAllowFragments tetapi bahkan jika mereka adalah objek autoreleased mungkin tidak memecahkan masalah ini.

Deepak GM
sumber
Deepak, Anda mendaftar NSJSONReadingMutableContainers dua kali. Apakah maksud Anda seseorang menjadi NSJSONReadingMutableLeaves?
jk7
0

contoh buruk, harus seperti ini {"id": 1, "name": "something as name"}

angka dan string dicampur.

pengguna1462586
sumber