簡體   English   中英

解析JSON時,Nil Coalescing返回nil而不是默認值

[英]Nil Coalescing returning nil instead of default value when parsing JSON

我正在解析JSON,當我使用nil合並嘗試為有時不存在的鍵/值集返回默認值時遇到問題。 我從中解析json的網站是http://heroesjson.com 創作者甚至給了我一張如何解析它的地圖。 當我嘗試解析人才關鍵時,就會遇到問題。 並非每個人才都有“冷卻時間”或“必備條件”鍵。 因此,當我嘗試將值分配給iCooldown或sPrerequisite時,如果鍵/值集不存在,則我嘗試使用nil合並來分配默認值,但嘗試拆開可選包裝時我意外發現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用作數組。 這會使您解開不存在的可選值。

從您的代碼:

//- talentsArray is a dictionary! 
if let talentsArray = dict[x]["talents"] as? Dictionary<String, AnyObject> {
    for y in 0 ..< dict.count {
        //- talentsArray["\(y)"] y is a value from 0 to the count, not necesarilly a key.
        if let allHeroTalents = talentsArray["\(y)"]{
            for z in 0 ..< allHeroTalents.count {
              // ...
            }
        }
    }
}

字典不維護順序,如果需要遍歷它們,可以枚舉字典,則必須for (key, value) in dictionary語法中使用for (key, value) in dictionary 類似於以下內容:

if let talents = dict[x]["talents"] as? Dictionary<String, AnyObject> {
    for (index, talent) in talents {
        for attribute in talent {
           // Do your thing.
        }
    }
}

據我了解,您的問題不是合並,而是字典DS的錯誤使用。 我復制了API調用,並且可以看到,即使您在將索引作為字典的鍵時將其遍歷的位置,也將遇到一些麻煩,因為鍵絕不是順序的:

在此處輸入圖片說明

更新

正如我在評論中提到的那樣,在您的解決方案有效的同時, let cooldown = item[cooldown]! 將強制解開您的可選值,這不是處理從API提取的可選值時的最佳方法(特別是如果它們不總是如您在原始問題中提到的那樣始終包含在數據中)。 假設您的“ cooldown”是一個雙精度字符,而“ prerequisite”是一個字符串,我將這樣實現它:

let cooldown = item["cooldown"] as? Double ?? 0.00
let prerequisite = item["prerequisite"] as? String ?? ""

此方法將在強制轉換為正確的預期類型時解開可選項,然后合並鍵值對的值。 這種方法更加安全,因為它不假定API調用將包含這些密鑰。

希望這可以幫助!

我弄亂了我的代碼,偶然發現了答案。 顯然,我需要做的只是解包(使用!)進入nil合並運算符的變量。

let cooldown = item["cooldown"]! ?? 0.00
let prerequisite = item["prerequisite"]! ?? ""

整個工作守則

 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: [String: [Talent]]? var abilities: [String: [Ability]]? 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 - parsing without this causes errors 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 talentsDict = dict[x]["talents"] as? Dictionary<String, AnyObject> { for (index, talentsAtLevel) in talentsDict { var talents = [Talent]() for item in talentsAtLevel as! [AnyObject] { let id = item["id"] let name = item["name"] let description = item["description"] let cooldown = item["cooldown"]! ?? 0.00 let prerequisite = item["prerequisite"]! ?? "" let icon = item["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!) talents.append(talent) } hero.talents = ["\\(index)": talents] } } //Parse Ability Class if let abilitiesDict = dict[x]["abilities"] as? Dictionary<String, AnyObject> { for (index, abilitiesAtLevel) in abilitiesDict { //index = heroid var abilities = [Ability]() for item in abilitiesAtLevel as! [AnyObject] { //item = ability let id = item["id"]! ?? "" let name = item["name"]! ?? "" let description = item["description"]! ?? "" let shortcut = item["shortcut"]! ?? "" let cooldown = item["cooldown"]! ?? 0.0 let manaCost = item["manaCost"]! ?? 0.0 let manaCostPerSecond = item["manaCostPerSecond"]! ?? 0.0 let aimType = item["aimType"]! ?? "" let heroic = item["heroic"]! ?? false let trait = item["trait"]! ?? false let mount = item["mount"]! ?? false let icon = item["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 ability = 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!) abilities.append(ability) } hero.abilities = ["\\(index)": abilities] } } heroes.append(hero) } } } } catch { print("Could not serialize") } } }.resume() } 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM