我正在解析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()。
//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;
答案 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调用,并且可以看到,即使你在将索引视为字典的键的同时迭代它,你也会遇到一些麻烦,因为键不是顺序的:
<强>更新强>
正如我在评论中提到的那样,当您的解决方案工作时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()
}