【问题标题】:Access the 2nd Level of Array in an Array Object of NSDictionary访问 NSDictionary 的数组对象中的第二级数组
【发布时间】:2019-02-22 02:15:50
【问题描述】:

我正在尝试NSDictionary 的数组对象中的第二级数组,但它返回 nil

我首先定义了NSDictionary,然后访问了第一级数组,直到现在一切都很好。

let dict = JSON(responseObject as! NSDictionary)
let arrayData = dict["data"].arrayObject! as NSArray
print("arrayData", arrayData)

打印arrayData 时,我得到以下返回

data =     (
{
categories = (
    {
    categories = "<null>";
    "category_id" = 333;
    image = "https://www.example.com/images/1.jpg";
    name = "Sub Category One";
    "parent_id" = 1000;
    },
    {
    categories = "<null>";
    "category_id" = 444;
    image = "https://www.example.com/images/2.jpg";
    name = "Sub Category Two";
    "parent_id" = 1000;
    },
    {
    categories = "<null>";
    "category_id" = 555;
    image = "https://www.example.com/images/3.jpg";
    name = "Sub Category Three";
    "parent_id" = 1000;
    }
);//end of categories array
"category_id" = 1000;
image = "https://www.examples.com/images/category.jpg";
name = "Parent Category";
"parent_id" = 0;
},
)

现在我正在尝试从上面返回的数组中访问和打印categories

所以我尝试了以下方法:

if dict["categories"].arrayObject != nil{
  let arrayData2 = dict["categories"].arrayObject! as NSArray
  print("arrayData2", arrayData2)
}

我也试过了

if dict["data"]["categories"].arrayObject != nil{
  let arrayData2 = dict["data"]["categories"].arrayObject! as NSArray
  print("arrayData2", arrayData2)
}

两种方式我都获得了 nil 价值,而它应该获得真正的价值。

已编辑:在 ViewController 中添加 API 调用和 dataModel 结构

视图控制器中调用HTTPAPI函数

func callingHttppApi(){
  DispatchQueue.main.async{
    NetworkManager.sharedInstance.showLoader()
    let sessionId = self.defaults.object(forKey:"token");
    self.categoriesTableView.isUserInteractionEnabled = false

      var requstParams = [String:Any]();
      requstParams["token"] = sessionId
      NetworkManager.sharedInstance.callingHttpRequest(params:requstParams, apiname:"api/cat", cuurentView: self){success,responseObject in
        if success == 1{
          let dict = responseObject as! NSDictionary;
          if dict.object(forKey: "fault") != nil{
            let fault = dict.object(forKey: "fault") as! Bool;
            if fault == true{
              self.loginRequest()
            }
          }else{
            NetworkManager.sharedInstance.dismissLoader()
            self.view.isUserInteractionEnabled = true
            self.categoriesTableView.isUserInteractionEnabled = true
            let dict = JSON(responseObject as! NSDictionary)
            let arrayData = dict["data"].arrayObject! as NSArray
            if dict["categories"].arrayObject != nil{
              let arrayData2 = dict["categories"].arrayObject! as NSArray
              print("arrayData2", arrayData2)
            }
            print("arrayData", arrayData)
            if dict["error"].intValue == 1{
              NetworkManager.sharedInstance.showErrorMessageWithBack(view: self, message: NetworkManager.sharedInstance.language(key: "error"))
            }else{
              self.categoriesCollModel = CategoriesViewModel(data:JSON(responseObject as! NSDictionary))
              dump (self.categoriesCollModel)
            }
          }
        }else if success == 2{
          NetworkManager.sharedInstance.dismissLoader()
          self.callingHttppApi()
        }
      }
    }
  }

用于转换值的 dataModel 结构

//
//  CategoriesViewModel.swift
//

import Foundation

class Categories: NSObject{
    var id:String = ""
    var name:String = ""
    var image:String = ""

    init(data:JSON){
        self.id = data["category_id"].stringValue
        self.name  = data["name"].stringValue
        self.image = data["image"].stringValue
    }

}

class SubCategories:NSObject{
    var pid:String = ""
    var id:String = ""
    var name:String = ""
    var image:String = ""

    init(data:JSON) {
        self.pid = data["parent_id"].stringValue
        self.id = data["category_id"].stringValue
        self.name  = data["name"].stringValue
        self.image = data["image"].stringValue
    }

}

struct Categories_Data{
    var id:String = ""
    var categoriesArray = [SubCategories]()

    init(data:JSON) {
        self.id = data["category_id"].stringValue
        if let arrayData = data["data"]["categories"].arrayObject {
            categoriesArray =  arrayData.map({(value) -> SubCategories in
                return  SubCategories(data:JSON(value))
            })
        }
    }

}

class CategoriesViewModel:NSObject{
    var categoryModel = [Categories]()
    var subCategories = [SubCategories]()
  var categories_Data = [Categories_Data]()

    init(data:JSON) {

        let arrayData = data["data"].arrayObject! as NSArray
        categoryModel =  arrayData.map({(value) -> Categories in
            return  Categories(data:JSON(value))
        })

        if data["data"]["categories"].arrayObject != nil{
            let arrayData2 = data["data"]["categories"].arrayObject! as NSArray
            categories_Data =  arrayData2.map({(value) -> Categories_Data in
                return  Categories_Data(data:JSON(value))
            })
        }

        //categoryModel = Categories(data:data)


    }

    var getCategories:Array<Categories>{
        return categoryModel
    }

  var getSubCategories:Array<SubCategories>{
        return subCategories
    }

}

【问题讨论】:

  • print("arrayData", arrayData) n 和 print("arrayData2", arrayData2) 得到什么?
  • 答案是显示返回...请检查行后打印arrayData时我得到以下返回我不能在这里发布返回它太长了。
  • 基本上不要在 Swift 中使用NSArray/NSDictionary。你扔掉了关键的类型信息。

标签: ios json swift nsdictionary arrayobject


【解决方案1】:

我强烈建议放弃SwiftyJSON 并使用Decodable

CategoriesViewModel 可以简化为

struct Root: Decodable {
    let data : [Category]
}

struct Category: Decodable {
    let categories : [Category]?
    let categoryId : Int
    let image : URL
    let name : String
    let parentId : Int
}

您需要从网络请求中获取原始数据(在以下名为data 的代码中),然后您可以简单地将数据解码为结构并使用点表示法获取值。

我添加了一个辅助函数,以便能够递归地打印嵌套类别

do {
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    let result = try decoder.decode(Root.self, from: data)
    printCategories(result.data)
} catch { print(error) }

func printCategories(_ categories : [Category]) {
    for item in categories {
        if let subCategories = item.categories {
            printCategories(subCategories)
        }
        print(item.name, item.categoryId)
    }
}

【讨论】:

    【解决方案2】:

    您还应该在初始化模型对象时使用guard 来验证值:

    init(data: JSON) {
        guard
            let id = data["category_id"] as? Int,
            let name = data["name"] as? String,
            let image = data["image"] as? String
            else {
                 return
            }
        self.id = id
        self.name  = name
        self.image = image
    }
    

    请注意,id 是一个整数值,而不是字符串,因此您应该更改模型。

    还可以查看Codable

    【讨论】:

    • 你的意思是 guard 会帮助将模型值转换回控制器中使用它们?
    • Guard 确保您期望的数据是正确的而不是零。如果类型不匹配,Guard 将失败,例如您声明您需要一个字符串,但提供的是一个整数。
    • 感谢您的解释,如上所述这是一个旧项目,我最终将对其进行更新,它基于 swift 3.2,很快我将转到 4 并了解有关 Codable 的更多信息
    • 我还建议确保您的模型符合 CustomStringConvertibleCustomDebugStringConvertible。然后,您可以更轻松地调试它们。
    【解决方案3】:

    您以错误的方式检索对象。始终使用检查 nil 值检索对象或值...
    检查以下代码以检索您的类别。

    let dict = JSON(responseObject as! NSDictionary)
        if let data = dict["data"] as? Array<Any>
        {
            if let categoryArray = data["categories"] as? Array<Dictionary<String,Any>>
            {
                if categoryArray.count > 0
                {
                    print(categoryArray)
                }
            }
        }
    

    【讨论】:

    • 非常感谢您的回答和支持。我编辑了我的问题并添加了callingHTTPAPI 函数,我如何调用 API 以获取 JSOn 响应,然后与 dataModel 共享它。我面临的两件事,数据模型只接收第一级数组,而没有像我的主要问题那样获得第二级。而且,一旦我在ViewController 中回调CategoriesViewModel,我就会得到nil 值,无法使用TableView 中的字符串,例如类别名称、图像和ID。
    • 第二级数组是什么?,在您的回复中只有一个数组。
    • 在响应中,第一级是data,它具有父类别,第二级是categories,如果找到则具有子类别而不是nil
    【解决方案4】:

    那么首先了解print("arrayData", arrayData)的回复。它是Dictionariesarray

    添加时:

    let arrayData = dict["data"].arrayObject! as NSArray
    

    您将数据保存在来自dictionary 的数组中。

    现在要获取您必须使用的类别arrayData

    let firstDict = arrayData[0] as! [String:Any]
    let arrayData2 = firstDict["categories"] as! [[String:Any]]
    print("arrayData2", arrayData2)
    

    我添加了这段代码来显示一个数据。

    更多数据可以用for循环:

    for dictData in arrayData {
        let arrayData2 = dictData["categories"] as! [[String:Any]]
        print("arrayData2", arrayData2)
    }
    

    并使用此循环,您可以管理或保存相关数据。

    还有一件事不要忘记添加 nil 处理部分。

    在使用 swift 时还有一件事不要使用 NSArrayNSDictionary

    尝试使用Codable。网上有很多例子,会让你的代码更干净。

    【讨论】:

    • 感谢您的回答,这是一个有点旧的项目,使用 swift 3.2 对其进行了更新。我很快就会转向 swift 4 并了解更多关于 Codable 的信息。我尝试了您的示例,它运行良好,但是您限制了对 arrayData[0] 的访问权限,我想保持动态并访问每个父类别的所有子类别
    • 更新了答案。请检查。
    • 如果我是正确的,这是代码结构 for dictData in arrayData { if dictData != nil{ let arrayData2 = dictData["categories"] as! [[String:Any]] print("arrayData2", arrayData2) } } 我得到的一件事是 Type 'Any' 没有下标成员
    • 因为Swift 3。此网址:stackoverflow.com/questions/39423367/… 将为您提供帮助。
    【解决方案5】:

    看起来很复杂,其实很简单,按照层次结构就行了

    例如,假设您想要访问“数据 -> 类别 -> 名称”,因为在您的回复中,您可以这样做

    这是包含所有内容的主词典。

    if let dict = responceObject as? NSDictionary {
    // now lets say you want go to your categories which is array of dictionaries
    
      if let arrDict = dict["categories"] as? NSArray {
          print("arrDict")
    
          // Now you can go anywhere in that array of dictionaries
          // For example you want to get name you can do like this
    
          for obj in arrDict {
             if let name = obj["name"] as? String {
                print(name)
             }
          }
      }
    }
    

    我在这里添加编辑部分

    // In your webcall lets say you receive a JSON(i am naming it as "json") object with type Any
    if let jsonObj = json as? [String: Any], let mainDict = jsonObj["data"] as? [[String: Any]] {
        for arr in mainDict {
           if let arrDict = arr["categories"] as? [[String: Any]] {
               print(arrDict)
               // Now you can dig that dict which is array of dictionaries
               for obj in arrDict {
                  if let name = obj["name"] as? String {
                     print(name)
                  }
    
                  if let id = obj["id"] as? Int { // Int or whatever it is
                     print(id)  
                  } 
               }
           }
        }
    }
    

    希望这会奏效。

    【讨论】:

    • 这只是一个帮助你的例子。希望它会有所帮助
    • 感谢我已经尝试过的示例,但“类别”的值却为零
    • 您想获得哪些类别?我的意思是字典数组或一个值为“”的字典?
    • 我有另一种方法,因为您的主词典是一个词典数组。如果你愿意,我可以编辑我的答案
    • 不是字典数组.. 如果您检查响应,您可以看到2个数组第一个data {},第二个是categories{},紧随其后的数据
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-07
    • 2021-06-09
    • 1970-01-01
    相关资源
    最近更新 更多