解析JSON我遇到問題,當我使用nil合併來嘗試並返回一個有時不存在的鍵/值集合的默認值時。我解析json的網站是http://heroesjson.com。創作者甚至給我一張如何解析它的地圖。我遇到問題的時候是我試圖解析人才的關鍵。並非每個人才都有「冷卻時間」或「先決條件」的關鍵。所以我嘗試使用nil合併來分配一個默認值,如果鍵/值集合不存在,但當我嘗試將值分配給iCooldown或sPrerequisite時,我會在嘗試展開可選參數時意外發現錯誤nil。自從我給它一個默認值以後,這怎麼可能?無解析JSON時合併返回nil而不是默認值
//Parse Talent Class
if let talentsArray = dict[x]["talents"] as? Dictionary<String, AnyObject> {
for y in 0 ..< dict.count {
if let allHeroTalents = talentsArray["\(y)"]{
for z in 0 ..< allHeroTalents.count {
let id = allHeroTalents[z]["id"]
let name = allHeroTalents[z]["name"]
let description = allHeroTalents[z]["description"]
let cooldown = allHeroTalents[z]["cooldown"] ?? 0.0
let prerequisite = allHeroTalents[z]["prerequisite"] ?? ""
let icon = allHeroTalents[z]["icon"]
let sId = id as? String
let sName = name as? String
let sDescription = description as? String
//let iCooldown = cooldown as! Double
//let sPrerequisite = prerequisite as! String
let sIcon = icon as? String
//let talent = Talent(id: sId!, name: sName!, description: sDescription!, cooldown: iCooldown, prerequisite: sPrerequisite, icon: sIcon!)
print("\(hero.name) has talent \(cooldown)")
}
}
}
}
我已將下面的整個文件包括在內以供參考。我的主文件只是調用func parseData()。
import Foundation
class Hero {
var id: String?
var attributeid: String?
var name: String?
var title: String?
var description: String?
var role: String?
var type: String?
var gender: String?
var franchise: String?
var difficulty: String?
var icon: String?
var ratings: Ratings?
var stats: Stats?
var talents: [Talent]?
init(id: String, attributeid: String, name: String, title: String, description: String, role: String, type: String, gender: String, franchise: String, difficulty: String, icon: String){
self.id = id
self.attributeid = attributeid
self.name = name
self.title = title
self.description = description
self.role = role
self.type = type
self.gender = gender
self.franchise = franchise
self.difficulty = difficulty
self.icon = icon
}
}
class Ratings {
var damage: Int?
var utility: Int?
var survivability: Int?
var complexity: Int?
init(damage: Int, utility: Int, survivability: Int, complexity: Int) {
self.damage = damage
self.utility = utility
self.survivability = survivability
self.complexity = complexity
}
}
class Stats {
var hp: Int?
var hpPerLevel: Int?
var hpRegen: Double?
var hpRegenPerLevel: Double?
var mana: Int?
var manaPerLevel: Int?
var manaRegen: Double?
var manaRegenPerLevel: Double?
init(hp: Int, hpPerLevel: Int, hpRegen: Double, hpRegenPerLevel: Double, mana: Int, manaPerLevel: Int, manaRegen: Double, manaRegenPerLevel: Double) {
self.hp = hp
self.hpPerLevel = hpPerLevel
self.hpRegen = hpRegen
self.hpRegenPerLevel = hpRegenPerLevel
self.mana = mana
self.manaPerLevel = manaPerLevel
self.manaRegen = manaRegen
self.manaRegenPerLevel = manaRegenPerLevel
}
}
class Talent {
var id: String?
var name: String?
var description: String?
var cooldown: Double?
var prerequisite: String?
var icon: String?
init(id: String, name: String, description: String, cooldown: Double, prerequisite: String, icon: String) {
self.id = id
self.name = name
self.description = description
self.cooldown = cooldown
self.prerequisite = prerequisite
self.icon = icon
}
}
class Ability {
var id: String?
var name: String?
var description: String?
var shortcut: String?
var cooldown: Double?
var manaCost: Double?
var manaCostPerSecond: Double?
var aimType: String?
var heroic: Bool?
var trait: Bool?
var mount: Bool?
var icon: String?
init(id: String, name: String, description: String, shortcut: String, cooldown: Double, manaCost: Double, manaCostPerSecond: Double, aimType: String, heroic: Bool, trait: Bool, mount: Bool, icon: String){
self.id = id
self.name = name
self.description = description
self.shortcut = shortcut
self.cooldown = cooldown
self.manaCost = manaCost
self.manaCostPerSecond = manaCostPerSecond
self.aimType = aimType
self.heroic = heroic
self.trait = trait
self.mount = mount
self.icon = icon
}
}
func parseData(){
let urlString = "http://heroesjson.com/heroes.json"
let session = NSURLSession.sharedSession()
let url = NSURL(string: urlString)!
session.dataTaskWithURL(url) { (data: NSData?, response:NSURLResponse?, error: NSError?) -> Void in
if let responseData = data {
do {
let json = try NSJSONSerialization.JSONObjectWithData(responseData, options: NSJSONReadingOptions.AllowFragments)
if let dict = json as? [Dictionary<String, AnyObject>] {
for x in 0 ..< dict.count {
if let id = dict[x]["id"], let attributeid = dict[x]["attributeid"], let name = dict[x]["name"], let title = dict[x]["title"], let description = dict[x]["description"], let role = dict[x]["role"], let type = dict[x]["type"], let gender = dict[x]["gender"], let franchise = dict[x]["franchise"], let difficulty = dict[x]["difficulty"], let icon = dict[x]["icon"] {
let hero = Hero(id: id as! String, attributeid: attributeid as! String, name: name as! String, title: title as! String, description: description as! String, role: role as! String, type: type as! String, gender: gender as! String, franchise: franchise as! String, difficulty: difficulty as! String, icon: icon as! String)
// Parse Ratings Class
if let dataArray = dict[x]["ratings"] as? Dictionary<String, Int> {
if let damage = dataArray["damage"], let utility = dataArray["utility"], let survivability = dataArray["damage"], let complexity = dataArray["complexity"] {
let rating = Ratings(damage: damage , utility: utility , survivability: survivability , complexity: complexity)
hero.ratings = rating
//print("\(hero.name) has a damage rating of \(hero.ratings!.damage)")
}
}
//Parse Stats Class
if let statsArray = dict[x]["stats"] as? Dictionary<String, AnyObject> {
if let dummy = statsArray[hero.id!]{//error handleing for vikings
if let hp = statsArray[hero.id!]!["hp"], let hpPerLevel = statsArray[hero.id!]!["hpPerLevel"], let hpRegen = statsArray[hero.id!]!["hpRegen"], let hpRegenPerLevel = statsArray[hero.id!]!["hpRegenPerLevel"], let mana = statsArray[hero.id!]!["mana"], let manaPerLevel = statsArray[hero.id!]!["manaPerLevel"], let manaRegen = statsArray[hero.id!]!["manaRegen"], let manaRegenPerLevel = statsArray[hero.id!]!["manaRegenPerLevel"] {
let stats = Stats(hp: hp as! Int, hpPerLevel: hpPerLevel as! Int, hpRegen: hpRegen as! Double, hpRegenPerLevel: hpRegenPerLevel as! Double, mana: mana as! Int, manaPerLevel: manaPerLevel as! Int, manaRegen: manaRegen as! Double, manaRegenPerLevel: manaRegenPerLevel as! Double)
hero.stats = stats
}
}//closes let dummy
}
//Parse Talent Class
if let talentsArray = dict[x]["talents"] as? Dictionary<String, AnyObject> {
for y in 0 ..< dict.count {
if let allHeroTalents = talentsArray["\(y)"]{
for z in 0 ..< allHeroTalents.count {
let id = allHeroTalents[z]["id"]
let name = allHeroTalents[z]["name"]
let description = allHeroTalents[z]["description"]
let cooldown = allHeroTalents[z]["cooldown"] ?? 0.0
let prerequisite = allHeroTalents[z]["prerequisite"] ?? ""
let icon = allHeroTalents[z]["icon"]
let sId = id as? String
let sName = name as? String
let sDescription = description as? String
//let iCooldown = cooldown as! Double
//let sPrerequisite = prerequisite as! String
let sIcon = icon as? String
//let talent = Talent(id: sId!, name: sName!, description: sDescription!, cooldown: iCooldown, prerequisite: sPrerequisite, icon: sIcon!)
print("\(hero.name) has talent \(cooldown)")
}
}
}
}
// Parse Ability Class
if let abilitiesArray = dict[x]["abilities"] as? Dictionary<String, AnyObject> {
for c in 0 ..< abilitiesArray.count {
for d in 0 ..< abilitiesArray.count {
if let dummy = abilitiesArray[hero.id!]{
let id = abilitiesArray[hero.id!]!["id"]
let name = abilitiesArray[hero.id!]!["name"]
let description = abilitiesArray[hero.id!]!["description"]
let shortcut = abilitiesArray[hero.id!]!["shortcut"]
let cooldown = abilitiesArray[hero.id!]!["cooldown"]
let manaCost = abilitiesArray[hero.id!]!["manaCost"]
let manaCostPerSecond = abilitiesArray[hero.id!]!["manaCostPerSecond"]
let aimType = abilitiesArray[hero.id!]!["aimType"]
let heroic = abilitiesArray[hero.id!]!["heroic"]
let trait = abilitiesArray[hero.id!]!["trait"]
let mount = abilitiesArray[hero.id!]!["mount"]
let icon = abilitiesArray[hero.id!]!["icon"]
let sId = id as? String
let sName = name as? String
let sDescription = description as? String
let sShortcut = shortcut as? String
let sCooldown = cooldown as? Double
let sManaCost = manaCost as? Double
let sManaCostPerSecond = manaCostPerSecond as? Double
let sAimType = aimType as? String
let sHeroic = heroic as? Bool
let sTrait = trait as? Bool
let sMount = mount as? Bool
let sIcon = icon as? String
// let abilities = Ability(id: sId!, name: sName!, description: sDescription!, shortcut: sShortcut!, cooldown: sCooldown!, manaCost: sManaCost!, manaCostPerSecond: sManaCostPerSecond!, aimType: sAimType!, heroic: sHeroic!, trait: sTrait!, mount: sMount!, icon: sIcon!)
}
}
}
}
heroes.append(hero)
}
}
}
} catch {
print("Could not serialize")
}
}
}.resume()
}
我建議幾件事情:你的解析數據功能越來越漂亮矮胖。我會建議提取'Dictionary'以將對象轉換爲初始化。例如,賦予'Ability'一個採用'[String:Any]'參數的初始化器,並自動爲其分配適當的成員。這也使得更改這些數據類變得更容易,因爲您可以添加一個成員,並將其賦值添加到初始化程序中,而無需繞過parseData和其他函數 – Alexander
此外,您的所有類成員都是可選的,var '變量。他們中的很多人看起來像他們可以是非可選和'let'常量。 – Alexander
@AlexanderMomchliov你可以給我一個例子,說明我會怎麼做(給一個初始化器帶一個[String:Any]參數,並自動分配成員)?我是新來的編碼,甚至不知道這是可能的。 – Apple