如何使用 NSJSONSerialization

Posted

技术标签:

【中文标题】如何使用 NSJSONSerialization【英文标题】:How to use NSJSONSerialization 【发布时间】:2011-12-02 13:14:15 【问题描述】:

我有一个 JSON 字符串(来自 phpjson_encode(),看起来像这样:

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

我想将其解析为我的 iPhone 应用程序的某种数据结构。我想对我来说最好的事情是拥有一个字典数组,所以数组中的第 0 个元素是一个带有键 "id" => "1""name" => "Aaa" 的字典。

我不明白NSJSONSerialization 是如何存储数据的。到目前为止,这是我的代码:

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

这只是我在另一个网站上看到的一个例子。我一直试图通过打印出元素的数量和类似的东西来读取JSON对象,但我总是得到EXC_BAD_ACCESS

我如何使用NSJSONSerialization来解析上面的JSON,并把它变成我提到的数据结构?

【问题讨论】:

你的 data 变量可能是 nil 不是,我已经测试过了。 有没有试过看看error对象有没有相关信息? 【参考方案1】:

您的根 json 对象不是字典而是数组:

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

这可能会让您清楚地了解如何处理它:

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

【讨论】:

谢谢,我会试试的,但 [JSON count] 不应该返回一些东西而不是只给我 EXC_BAD_ACCESS 吗? 应该,这就是为什么我添加了检查 !jsonArray 并打印出错误。这应该会显示解析期间发生的任何错误。 @xs2bush 不,因为你没有创建 jsonArray 它应该是自动释放的。 @Logan:是的,[JSON 计数] 应该返回一个值。请参阅下面关于僵尸的回答。 EXC_BAD_ACCESS 几乎总是与僵尸有关。 在这种情况下,item 是给定 JSON 键值对中的键。您的 for 循环完美地输出了我的每个 JSON 键。但是,我已经知道我想要的值的键,即“键”。我获取此键的值并将其输出到日志的努力失败了。任何进一步的见解?【参考方案2】:

这是我检查接收到的 json 是数组还是字典的代码:

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

我已经尝试过选项:kNilOptions 和 NSJSONReadingMutableContainers 并且两者都可以正常工作。

显然,实际代码不可能是我在 if-else 块中创建 NSArray 或 NSDictionary 指针的这种方式。

【讨论】:

【参考方案3】:

它对我有用。您的 data 对象可能是 nil 并且,正如 rckoenes 所指出的,根对象应该是一个(可变)数组。请参阅此代码:

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

(我不得不用反斜杠转义 JSON 字符串中的引号。)

【讨论】:

【参考方案4】:

除了结果是NSArray,而不是NSDictionary,您的代码看起来不错,这是一个示例:

前两行只是用 JSON 创建了一个数据对象,就像你从网上读取它一样。

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 内容(字典列表):

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

【讨论】:

这个选项(NSJSONReadingMutableContainers)是什么意思。我没有 kNilOption,一切正常。告诉我使用这些选项的目的 Google 中的热门搜索:NSJSONReadingMutableLeaves:“指定 JSON 对象图中的叶字符串创建为 NSMutableString 的实例。” MutableContainer 呢 糟糕,再次从 Google 搜索结果顶部:NSJSONReadingMutableContainers:“指定将数组和字典创建为可变对象。” 这些仅在您计划修改返回的 JSON 对象并将其保存回来时才有帮助。在任何一种情况下,对象都可能是自动释放的对象,这似乎是根本原因。【参考方案5】:
["id": "1", "name":"Aaa", "id": "2", "name":"Bbb"]

在上面的 JSON 数据中,您表明我们有一个包含字典数量的数组。

你需要使用这段代码来解析它:

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

对于 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"])
    

【讨论】:

【参考方案6】:

以下代码从网络服务器获取 JSON 对象,并将其解析为 NSDictionary。我使用了 openweathermap API,它为这个例子返回一个简单的 JSON 响应。为简单起见,此代码使用同步请求。

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

【讨论】:

我认为你的答案应该是最好的答案,因为它似乎是访问 JSON 结构的最快方式。 选项不应使用两个 |但单|因为它们需要按位或。 这个问题没有问任何关于网络请求的内容【参考方案7】:

@rckoenes 已经向您展示了如何正确地从 JSON 字符串中获取数据。

对于您提出的问题:EXC_BAD_ACCESS 几乎总是在您尝试访问已 [自动] 释放的对象时出现。这并不特定于 JSON [反] 序列化,而是与您获取一个对象然后在它被释放后访问它有关。它来自 JSON 的事实并不重要。

有很多页面描述了如何调试这个——你想谷歌(或 SO)obj-c zombie objects,尤其是NSZombieEnabled,这将证明对你帮助确定僵尸的来源非常宝贵对象。 (“Zombie”是当你释放一个对象但保留一个指向它的指针并稍后尝试引用它时调用它。)

【讨论】:

【参考方案8】:

Xcode 7(Beta)上的 Swift 2.0 带有 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)
  

【讨论】:

【参考方案9】:

注意:对于 Swift 3。 您的 JSON 字符串返回 Array 而不是 Dictionary。请尝试以下方法:

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

【讨论】:

【参考方案10】:
#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];

【讨论】:

【参考方案11】:

问题似乎与对象的自动释放有关。 NSJSONSerialization JSONObjectWithData 显然是在创建一些自动释放的对象并将其传递给您。如果您尝试将其带到不同的线程,它将无法工作,因为它无法在不同的线程上释放。

诀窍可能是尝试制作该字典或数组的可变副本并使用它。

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

将 NSDictionary 视为 NSArray 不会导致 Bad access 异常,而是在进行方法调用时可能会崩溃。

另外,这里的选项可能并不重要,但最好提供 NSJSONReadingMutableContainers | NSJSONReadingMutableContainers | NSJSONReadingAllowFragments 但即使它们是自动释放的对象也可能无法解决此问题。

【讨论】:

Deepak,你列出了两次 NSJSONReadingMutableContainers。你的意思是 NSJSONReadingMutableLeaves 吗?【参考方案12】:

不好的例子,应该是这样的 "id":1, "name":"something as name"

数字和字符串混合在一起。

【讨论】:

以上是关于如何使用 NSJSONSerialization的主要内容,如果未能解决你的问题,请参考以下文章

如何使用本机反应创建登录以及如何验证会话

如何在自动布局中使用约束标识符以及如何使用标识符更改约束? [迅速]

如何使用 AngularJS 的 ng-model 创建一个数组以及如何使用 jquery 提交?

如何使用laravel保存所有行数据每个行名或相等

如何使用 Math.Net 连接矩阵。如何使用 Math.Net 调用特定的行或列?

WSARecv 如何使用 lpOverlapped?如何手动发出事件信号?