访问字典数组 Swift 3
Posted
技术标签:
【中文标题】访问字典数组 Swift 3【英文标题】:Access array of dictionaries Swift 3 【发布时间】:2017-09-21 04:24:04 【问题描述】:我需要访问字典数组中的工作数据,对此我有点困惑。我正在使用 swift 3。有人可以给我一些代码来完成它吗?
我在用这个
let work: NSArray! = fbData.value(forKey: "work") as! NSArray
if let position: NSArray = work[0] as! NSArray
let positionName: String = position.value(forKey: "name") as! String
self.userWorkExpLabel.text = "\(positionName)" as String
但我有这个答案: 无法将“__NSDictionaryI”(0x1106c7288)类型的值转换为“NSArray”(0x1106c6e28)。
有 API
"work": [
"employer":
"id": "93643283467",
"name": "Oracast"
,
"location":
"id": "111983945494775",
"name": "Calgary, Alberta"
,
"position":
"id": "146883511988628",
"name": "Mobile Developer"
,
"start_date": "2017-04-30",
"id": "1446626725564198"
],
好的,伙计们。我尝试了你发布的内容,我现在拥有的是这样的:
结构类:
import Foundation
struct Worker
let employer: Employer
let location: Location
let position: Position
let startDate:String
let id: String
init?(fromDict dict: Dictionary<String, Any>)
guard let employer = Employer(fromDict: dict["employer"] as? Dictionary<String, String>),
let location = Location(fromDict: dict["location"] as? Dictionary<String, String>),
let position = Position(fromDict: dict["position"] as? Dictionary<String, String>),
let startDate = dict["start_date"] as? String,
let id = dict["id"] as? String else
return nil
self.employer = employer
self.location = location
self.position = position
self.startDate = startDate
self.id = id
struct Employer
let id: String
let name: String
init?(fromDict dict:Dictionary<String, String>?)
guard let id = dict?["id"],
let name = dict?["name"] else
return nil
self.id = id
self.name = name
struct Location
let id:String
let name:String
init?(fromDict dict:Dictionary<String, String>?)
guard let id = dict?["id"],
let name = dict?["name"] else
return nil
self.id = id
self.name = name
struct Position
let id:String
let name:String
init?(fromDict dict:Dictionary<String, String>?)
guard let id = dict?["id"],
let name = dict?["name"] else
return nil
self.id = id
self.name = name
我创建了一个名为 facebookGraphRequest 的类。
import Foundation
import UIKit
import FBSDKCoreKit
import FBSDKLoginKit
import FBSDKShareKit
class facebookGraphRequest: NSObject
class func graphRequestWork(completion: @escaping(_ error: Error?, _ facebookUserWork: Worker)-> Void)
if ((FBSDKAccessToken.current()) != nil)
let parameters = ["fields": "name, picture.width(198).height(198), locationlocation, workemployer, education, about, id"]
let graphRequest: FBSDKGraphRequest = FBSDKGraphRequest(graphpath: "me", parameters: parameters)
graphRequest.start (connection, result, error) in
if ((error) != nil )
print(error!)
else
print(result!)
func workersArray(data:Dictionary<String, Any>)->[Worker]?
guard let arrayOfDict = data["work"] as? Array<Dictionary<String, Any>> else
return nil
return arrayOfDict.flatMap( Worker(fromDict: $0))
我在 viewController 中调用这个数据:
func facebookLogin()
facebookGraphRequest.graphRequestWork (error: Error?, facebookUserWork: Worker) in
self.userNameJobPositionLabel.text = "\(facebookUserWork.position)"
self.companyNameLabel.text = "\(facebookUserWork.employer)"
有人知道发生了什么吗?标签没有任何反应。
我认为这个 api 比这更容易。我真的对这个过程感到困惑......对不起,如果它看起来像愚蠢的问题,但我真的因为这些事情搞砸了......我真的需要你们的帮助。我的工作取决于:(
【问题讨论】:
首先,千万不要在 Swift 中使用NSArray
或 NSDictionary
,除非你真的必须这样做。其次,你有一个字典数组,所以work[0]
是字典,而不是数组,这就是你强制向下转换失败的原因。
【参考方案1】:
在尝试了 Swift 4 并朝着 @PuneetSharma 展示的方向前进后,我发现使用原始 JSON 文本 Codable
和 JSONDecoder
会更容易:
import Foundation
// define the nested structures
struct Work: Codable
let work: [Worker]
struct Worker: Codable
let employer: Employer
let location: Location
let position: Position
let startDate: String
let id: String
// needed a custom key for start_date
enum CodingKeys : String, CodingKey
case employer, location, position, startDate = "start_date", id
struct Employer: Codable
let id: String
let name: String
struct Location: Codable
let id: String
let name: String
struct Position: Codable
let id: String
let name: String
// turn the text into `Data` and then
// decode as the outermost structure
if let jsonData = json.data(using: .utf8),
let work = try? JSONDecoder().decode(Work.self, from: jsonData)
print(work)
结果是一个包含所有数据的Work
结构:
Work(work: [
Model.Worker(employer : Model.Employer(id : "93643283467",
name: "Oracast"),
location : Model.Location(id : "111983945494775",
name: "Calgary, Alberta"),
position : Model.Position(id : "146883511988628",
name: "Mobile Developer"),
startDate: "2017-04-30",
id : "1446626725564198")
])
(我对输出进行了一些格式化以阐明生成的结构。)
您只需使用Codable
即可免费获得许多功能。反过来,从任何结构中生成 JSON 文本也很简单。
【讨论】:
【参考方案2】:理想情况下,您应该像这样引入模型类:
struct Worker
let employer:Employer
let location:Location
let position:Position
let startDate:String
let id:String
init?(fromDict dict:Dictionary<String, Any>)
guard let employer = Employer(fromDict: dict["employer"] as? Dictionary<String, String>), let location = Location(fromDict: dict["location"] as? Dictionary<String, String>), let position = Position(fromDict: dict["position"] as? Dictionary<String, String>), let startDate = dict["start_date"] as? String, let id = dict["id"] as? String else
return nil
self.employer = employer
self.location = location
self.position = position
self.startDate = startDate
self.id = id
struct Employer
let id:String
let name:String
init?(fromDict dict:Dictionary<String, String>?)
guard let id = dict?["id"], let name = dict?["name"] else
return nil
self.id = id
self.name = name
struct Location
let id:String
let name:String
init?(fromDict dict:Dictionary<String, String>?)
guard let id = dict?["id"], let name = dict?["name"] else
return nil
self.id = id
self.name = name
struct Position
let id:String
let name:String
init?(fromDict dict:Dictionary<String, String>?)
guard let id = dict?["id"], let name = dict?["name"] else
return nil
self.id = id
self.name = name
现在,你可以像这样引入一个函数:
func workersArray(data:Dictionary<String, Any>)->[Worker]?
guard let arrayOfDict = data["work"] as? Array<Dictionary<String, Any>> else
return nil
return arrayOfDict.flatMap( Worker(fromDict: $0))
【讨论】:
看起来很干净。我唯一要添加的是enum EmployerKey: String case id, name
之类的东西,所以有具体的字符串来匹配字典键。像dict?[EmployerKey.name.rawValue]
一样使用它们
@ColGraff:我同意,这将以一种很好的方式将硬编码的字符串包装在枚举中。感谢您的建议。
@ColGraff:嵌套枚举是应该做的。随着 Swift 引入 Encodable 和 Decodable 协议,序列化再次变得有趣:)。 OP 已经接受了另一个答案,所以我没有编辑这个答案,但肯定会在我的项目中使用嵌套枚举。
事实上,我完全忘记了 struct
声明的 Codable
会自动获得 CodingKeys
下的密钥。所以跳过enum
直接使用struct Employer: Codable ...
然后你就可以使用CodingKeys.name.stringValue
@ColGraff:是的,但您可能希望提供自定义键和嵌套枚举是这样做的方法:)【参考方案3】:
使用此代码
if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any]
if let workArray = json["work"] as? [[String: Any]]
if let dictWork = workArray.first
if let dictPosition = dictWork["position"] as? [String: String]
print("position name : \(dictPosition["name"])")
【讨论】:
它给出一个错误:无法将类型 '(,,_) throws -> ()' 的值转换为 FBSDKGraphRequestHandler 类型的预期参数。 如果您有很多嵌套的if let
语句,您可以通过用逗号分隔它们而不是嵌套它们来使其更简单。以上是关于访问字典数组 Swift 3的主要内容,如果未能解决你的问题,请参考以下文章