繁体   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