Cách sử dụng NSJSONSerialization


156

Tôi có một chuỗi JSON (từ PHP json_encode()trông như thế này:

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

Tôi muốn phân tích điều này thành một số loại cấu trúc dữ liệu cho ứng dụng iPhone của tôi. Tôi đoán điều tốt nhất đối với tôi là có một mảng từ điển, vì vậy phần tử thứ 0 trong mảng là một từ điển có khóa "id" => "1""name" => "Aaa".

Tôi không hiểu làm thế nào các NSJSONSerializationcửa hàng dữ liệu mặc dù. Đây là mã của tôi cho đến nay:

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

Đây chỉ là một cái gì đó tôi thấy như một ví dụ trên một trang web khác. Tôi đã cố gắng để có được một đọc trên JSONđối tượng bằng cách in ra số lượng các yếu tố và những thứ như thế, nhưng tôi luôn luôn nhận được EXC_BAD_ACCESS.

Làm cách nào để sử dụng NSJSONSerializationđể phân tích JSON ở trên và biến nó thành cấu trúc dữ liệu mà tôi đã đề cập?


biến dữ liệu của bạn có thể là không
d.lebedev

Không, tôi đã kiểm tra rồi.
Logan Serman

Bạn đã thử xem có bất kỳ thông tin liên quan trong đối tượng lỗi không?
Monolo

Câu trả lời:


214

Đối tượng json gốc của bạn không phải là một từ điển mà là một mảng:

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

Điều này có thể cung cấp cho bạn một hình ảnh rõ ràng về cách xử lý nó:

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

Cảm ơn tôi sẽ thử điều đó, nhưng không nên [JSON count]trả lại thứ gì đó thay vì chỉ cho tôi EXC_BAD_ACCESS?
Logan Serman

Nó nên, đó là lý do tại sao tôi thêm kiểm tra nếu !jsonArrayvà in ra lỗi. Điều này sẽ hiển thị bất kỳ lỗi xảy ra trong quá trình phân tích cú pháp.
RCkoenes

1
@ xs2bush không, vì bạn không tạo jsonArraynên nó sẽ tự động chạy.
RCkoenes

@Logan: Có, [Số lượng JSON] sẽ trả về một giá trị. Xem câu trả lời của tôi dưới đây liên quan đến zombie. EXC_BAD_ACCESS hầu như luôn liên quan đến zombie.
Olie

Trong trường hợp này, mục là khóa trong cặp giá trị khóa JSON đã cho .. vòng lặp for của bạn hoạt động hoàn hảo xuất ra từng khóa JSON của tôi. Tuy nhiên tôi đã biết khóa cho giá trị tôi muốn, cụ thể là 'khóa'. Những nỗ lực của tôi để có được giá trị của khóa này và đưa nó vào nhật ký đã thất bại. Còn cái nhìn sâu sắc nào nữa không?
Thomas Clowes

75

Đây là mã của tôi để kiểm tra xem json nhận được là một mảng hoặc từ điển:

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

Tôi đã thử điều này cho các tùy chọn: kNilOptions và NSJSONReadMutableContainers và hoạt động chính xác cho cả hai.

Rõ ràng, mã thực tế không thể theo cách này khi tôi tạo con trỏ NSArray hoặc NSDipedia trong khối if-other.


29

Nó làm việc cho tôi. dataĐối tượng của bạn có lẽ nilvà, như RCkoenes đã lưu ý, đối tượng gốc phải là một mảng (có thể thay đổi). Xem mã này:

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

(Tôi đã phải thoát các trích dẫn trong chuỗi JSON bằng dấu gạch chéo ngược.)


9

Mã của bạn có vẻ ổn ngoại trừ kết quả là một NSArray, không phải NSDictionary, đây là một ví dụ:

Hai dòng đầu tiên chỉ tạo một đối tượng dữ liệu bằng JSON, giống như bạn sẽ đọc nó từ mạng.

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

Nội dung NSLog (danh sách từ điển):

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

Tùy chọn này (NSJSONReadMutableContainers) nghĩa là gì. Tôi không kNilOption và mọi thứ hoạt động tốt. Hãy cho tôi biết mục đích của việc sử dụng các tùy chọn này
Zar E Ahmer

Lượt truy cập hàng đầu trong Google :: NSJSONReadingMutableLeaves"Chỉ định rằng các chuỗi lá trong biểu đồ đối tượng JSON được tạo như các phiên bản của NSMutableString."
zaph

và những gì về MutableContainer
Zar E Ahmer

Rất tiếc, một lần nữa từ kết quả hàng đầu của Google NSJSONReadingMutableContainers:: "Chỉ định rằng các mảng và từ điển được tạo như các đối tượng có thể thay đổi."
zaph

1
Những điều này chỉ có ích nếu bạn có kế hoạch sửa đổi đối tượng JSON được trả về và lưu lại. Trong cả hai trường hợp, các đối tượng có thể là đối tượng tự động phát hành và đó dường như là nguyên nhân gốc rễ.
Deepak GM

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

Trong dữ liệu JSON ở trên, bạn đang cho thấy rằng chúng ta có một mảng chứa số lượng từ điển.

Bạn cần sử dụng mã này để phân tích cú pháp:

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

Đối với nhanh chóng 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

Đoạn mã sau tìm nạp một đối tượng JSON từ máy chủ web và phân tích cú pháp nó thành NSDipedia. Tôi đã sử dụng API openweathermap trả về một phản hồi JSON đơn giản cho ví dụ này. Để giữ cho nó đơn giản, mã này sử dụng các yêu cầu đồng bộ.

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

Tôi nghĩ rằng câu trả lời của bạn nên là câu trả lời tốt nhất vì đây có vẻ là cách nhanh nhất để truy cập cấu trúc JSON.
Porizm

2
Các tùy chọn không nên sử dụng hai | nhưng một | vì chúng cần phải được OR bitwise.
Deepak GM

Câu hỏi không hỏi bất cứ điều gì về các yêu cầu mạng
Noah Gilmore

2

@rckoenes đã chỉ cho bạn cách lấy chính xác dữ liệu của bạn từ chuỗi JSON.

Đối với câu hỏi bạn đã hỏi: EXC_BAD_ACCESShầu như luôn luôn xuất hiện khi bạn cố gắng truy cập một đối tượng sau khi nó được [tự động] phát hành. Điều này không đặc trưng cho tuần tự hóa [de-] JSON, nhưng, thay vào đó, chỉ cần bạn thực hiện một đối tượng và sau đó truy cập vào nó sau khi nó được phát hành. Thực tế là nó xuất hiện thông qua JSON không thành vấn đề.

Có rất nhiều trang mô tả cách gỡ lỗi này - bạn muốn Google (hoặc SO) obj-c zombie objectsvà đặc biệt NSZombieEnabled, điều này sẽ chứng minh vô giá cho bạn trong việc giúp xác định nguồn gốc của các đối tượng zombie của bạn. ("Zombie" là tên gọi của nó khi bạn phát hành một đối tượng nhưng giữ một con trỏ tới nó và cố gắng tham chiếu nó sau.)


1

Swift 2.0 trên Xcode 7 (Beta) với khối 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

LƯU Ý: Đối với Swift 3 . Chuỗi JSON của bạn đang trả về Mảng thay vì Từ điển. Vui lòng thử các cách sau:

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

Vấn đề dường như là với autorelease của các đối tượng. NSJSONSerialization JSONObjectWithData rõ ràng đang tạo ra một số đối tượng tự động phát hành và gửi lại cho bạn. Nếu bạn cố gắng đưa nó vào một luồng khác, nó sẽ không hoạt động vì nó không thể được xử lý trên một luồng khác.

Thủ thuật có thể là thử làm một bản sao có thể thay đổi của từ điển hoặc mảng đó và sử dụng nó.

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

Việc coi một NSDipedia là NSArray sẽ không dẫn đến ngoại lệ truy cập xấu mà thay vào đó có thể sẽ bị sập khi thực hiện cuộc gọi phương thức.

Ngoài ra, có thể các tùy chọn không thực sự quan trọng ở đây nhưng tốt hơn là cung cấp cho NSJSONReadMutableContainers | NSJSONReadMutableContainers | NSJSONRead ALLowFragments nhưng ngay cả khi chúng là đối tượng tự động phát hành, nó có thể không giải quyết được vấn đề này.


Deepak, bạn đã liệt kê NSJSONReadMutableContainers hai lần. Ý của bạn là một NSJSONReadMutableLeaves?
jk7

0

ví dụ xấu, nên là một cái gì đó như thế này {"id": 1, "name": "cái gì đó như tên"}

số và chuỗi được trộn lẫn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.