在解析JSON时,Nil Coalescing返回nil而不是默认值

时间:2016-08-22 01:10:03

标签: json swift

我正在解析JSON,当我使用nil coalescing尝试返回有时不存在的键/值集的默认值时,我遇到了问题。我解析json的网站是http://heroesjson.com。创作者甚至给了我一张如何解析它的地图。我遇到问题的地方是当我试图解析人才关键时。不是每个人都有冷却时间"或者"先决条件"键。所以我尝试使用nil合并来分配默认值,如果键/值集不存在但是当我尝试将值分配给iCooldown或sPrerequisite时,我在尝试解包可选时意外地发现错误nil。怎么可能因为我给它一个默认值?

import re

def less10(string):
    return int(string) - 10

def replacer(match):
    return '%s%d,%d%s' % (match.group('prefix'),
                          less10(match.group('x')),
                          less10(match.group('y')),
                          match.group('suffix'))

print re.sub(r'(?P<prefix><stuff[^>]*translate\()(?P<x>\d*),(?P<y>\d*)(?P<suffix>\)/>)',
             replacer,
             '<stuff translate(100,200)/>')

我已将整个文件包含在下面以供参考。我的主文件只调用func parseData()。

&#13;
&#13;
//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)")


                                    }
                                }
                            }

                        }
&#13;
&#13;
&#13;

2 个答案:

答案 0 :(得分:3)

我可能错了,但似乎您正在使用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语法。有点像:

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

我理解你的问题,不是合并,而是字典DS的错误使用。我复制了API调用,并且可以看到,即使你在将索引视为字典的键的同时迭代它,你也会遇到一些麻烦,因为键不是顺序的:

enter image description here

<强>更新

正如我在评论中提到的那样,当您的解决方案工作时let cooldown = item[cooldown]!会强制解包您的可选项,这不是处理从API获取的可选值时的最佳方法(特别是如果它们并不总是包含在API中)你在原始问题中提到的数据)。假设你的冷却时间为&#39;是一个双重和先决条件&#39;是一个字符串,我会像这样实现它:

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

此方法将在转换为正确的预期类型时解包您的可选项,然后合并键值对的值。这种方法更安全,因为它不假设API调用将包含这些键。

希望这有帮助!

答案 1 :(得分:-1)

我弄乱了我的代码,偶然发现了答案。显然,我需要做的就是将变量解包(使用!)进入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()
}