2017-11-10 1 views
0

디코딩 가능한 프로토콜을 사용하여 Swift 4에 DotA2 iOS 응용 프로그램을 만드는 동안이 오류가 발생했습니다.JSON 디코더 - 디코딩 오류 (문자열 포함)

keyNotFound(DotaPal.MatchPlayerDetail.CodingKeys.personaname, Swift.DecodingError.Context(codingPath: [DotaPal.MatchDetail.CodingKeys.players, Foundation.(_JSONKey in _12768CA107A31EF2DCE034FD75B541C9)(stringValue: "Index 0", intValue: Optional(0))], debugDescription: "No value associated with key personaname (\"personaname\").", underlyingError: nil))

이유는 모르겠지만 "personaname"을 (를) 해독하는 데 문제가있는 것으로 보입니다. 과거에는 더 복잡한 json으로 디코더를 사용했지만 잠시 보았을 때이 문제에 대한 해결책을 찾지 못했습니다 : /.

여기에는 디코딩하려는 json에 대한 링크뿐만 아니라 해당 전체에 관련된 구조체가 있습니다. 세 번째 플레이어 (인덱스 2)가 personaname 속성을 가지고 있지 않기 때문이다 JSON Attempting to be Decoded

import Foundation 

struct MatchDetail { 
    var match_id: Int? 
    var barracks_status_dire: Int? 
    var barracks_status_radiant: Int? 
    var cluster: Int? 
    var dire_score: Int? 
    var duration: Int? 
    var first_blood_time: Int? 
    var game_mode: Int? 
    var radiant_score: Int? 
    var radiant_win: Int? 
    var skill: Int? 
    var start_time: Int? 
    var tower_status_dire: Int? 
    var tower_status_radiant: Int? 
    var players: [MatchPlayerDetail]? 
} 

extension MatchDetail: Decodable { 
    enum CodingKeys: String, CodingKey { 
     case match_id 
     case barracks_status_dire 
     case barracks_status_radiant 
     case cluster 
     case dire_score 
     case duration 
     case first_blood_time 
     case game_mode 
     case radiant_score 
     case radiant_win 
     case skill 
     case start_time 
     case tower_status_dire 
     case tower_status_radiant 
     case players 
    } 

    init(from decoder: Decoder) throws { 
     let values = try decoder.container(keyedBy: CodingKeys.self) 
     match_id = try values.decode(Int?.self, forKey: .match_id) 
     barracks_status_dire = try values.decode(Int?.self, forKey: .barracks_status_dire) 
     barracks_status_radiant = try values.decode(Int?.self, forKey: .barracks_status_radiant) 
     cluster = try values.decode(Int?.self, forKey: .cluster) 
     dire_score = try values.decode(Int?.self, forKey: .dire_score) 
     duration = try values.decode(Int?.self, forKey: .duration) 
     first_blood_time = try values.decode(Int?.self, forKey: .first_blood_time) 
     game_mode = try values.decode(Int?.self, forKey: .game_mode) 
     radiant_score = try values.decode(Int?.self, forKey: .radiant_score) 
     radiant_win = try values.decode(Int?.self, forKey: .radiant_win) 
     skill = try values.decode(Int?.self, forKey: .skill) 
     start_time = try values.decode(Int?.self, forKey: .start_time) 
     tower_status_dire = try values.decode(Int?.self, forKey: .tower_status_dire) 
     tower_status_radiant = try values.decode(Int?.self, forKey: .tower_status_radiant) 
     players = try values.decode([MatchPlayerDetail]?.self, forKey: .players) 
    } 
} 

struct MatchPlayerDetail { 
    var match_id: Int? 
    var player_slot: Int? 
    var ability_upgrades_arr: [Int?] 
    var account_id: Int? 
    var assists: Int? 
    var backpack_0: Int? 
    var backpack_1: Int? 
    var backpack_2: Int? 
    var deaths: Int? 
    var denies: Int? 
    var gold: Int? 
    var gold_per_min: Int? 
    var gold_spent: Int? 
    var hero_damage: Int? 
    var hero_healing: Int? 
    var hero_id: Int? 
    var item_0: Int? 
    var item_1: Int? 
    var item_2: Int? 
    var item_3: Int? 
    var item_4: Int? 
    var item_5: Int? 
    var kills: Int? 
    var last_hits: Int? 
    var leaver_status: Int? 
    var level: Int? 
    var xp_per_min: Int? 
    var personaname: String? 
    var radiant_win: Bool? 
    var isRadiant: Bool? 
    var win: Int? 
    var lose: Int? 
    var total_gold: Int? 
    var total_xp: Int? 
    var kda: Int? 
    var abandons: Int? 
} 

extension MatchPlayerDetail: Decodable { 
    enum CodingKeys: String, CodingKey { 
     case match_id 
     case player_slot 
     case ability_upgrades_arr 
     case account_id 
     case assists 
     case backpack_0 
     case backpack_1 
     case backpack_2 
     case deaths 
     case denies 
     case gold 
     case gold_per_min 
     case gold_spent 
     case hero_damage 
     case hero_healing 
     case hero_id 
     case item_0 
     case item_1 
     case item_2 
     case item_3 
     case item_4 
     case item_5 
     case kills 
     case last_hits 
     case leaver_status 
     case level 
     case xp_per_min 
     case personaname 
     case radiant_win 
     case isRadiant 
     case win 
     case lose 
     case total_gold 
     case total_xp 
     case kda 
     case abandons 
    } 

    init(from decoder: Decoder) throws { 
     let values = try decoder.container(keyedBy: CodingKeys.self) 
     match_id = try values.decode(Int?.self, forKey: .match_id) 
     player_slot = try values.decode(Int?.self, forKey: .player_slot) 
     ability_upgrades_arr = try values.decode([Int?].self, forKey: .ability_upgrades_arr) 
     assists = try values.decode(Int?.self, forKey: .assists) 
     backpack_0 = try values.decode(Int?.self, forKey: .backpack_0) 
     backpack_1 = try values.decode(Int?.self, forKey: .backpack_1) 
     backpack_2 = try values.decode(Int?.self, forKey: .backpack_2) 
     deaths = try values.decode(Int?.self, forKey: .deaths) 
     denies = try values.decode(Int?.self, forKey: .denies) 
     gold = try values.decode(Int?.self, forKey: .gold) 
     gold_per_min = try values.decode(Int?.self, forKey: .gold_per_min) 
     gold_spent = try values.decode(Int?.self, forKey: .gold_spent) 
     hero_damage = try values.decode(Int?.self, forKey: .hero_damage) 
     hero_healing = try values.decode(Int?.self, forKey: .hero_healing) 
     hero_id = try values.decode(Int?.self, forKey: .hero_id) 
     item_0 = try values.decode(Int?.self, forKey: .item_0) 
     item_1 = try values.decode(Int?.self, forKey: .item_1) 
     item_2 = try values.decode(Int?.self, forKey: .item_2) 
     item_3 = try values.decode(Int?.self, forKey: .item_3) 
     item_4 = try values.decode(Int?.self, forKey: .item_4) 
     item_5 = try values.decode(Int?.self, forKey: .item_5) 
     kills = try values.decode(Int?.self, forKey: .kills) 
     last_hits = try values.decode(Int?.self, forKey: .last_hits) 
     leaver_status = try values.decode(Int?.self, forKey: .leaver_status) 
     level = try values.decode(Int?.self, forKey: .level) 
     xp_per_min = try values.decode(Int?.self, forKey: .xp_per_min) 
     personaname = try values.decode(String?.self, forKey: .personaname) 
     radiant_win = try values.decode(Bool?.self, forKey: .radiant_win) 
     isRadiant = try values.decode(Bool?.self, forKey: .isRadiant) 
     win = try values.decode(Int?.self, forKey: .win) 
     lose = try values.decode(Int?.self, forKey: .lose) 
     total_gold = try values.decode(Int?.self, forKey: .total_gold) 
     total_xp = try values.decode(Int?.self, forKey: .total_xp) 
     kda = try values.decode(Int?.self, forKey: .kda) 
     abandons = try values.decode(Int?.self, forKey: .abandons) 
    } 
} 
+1

OMG, 많은 물음표. 값을 1 : 1로 디코딩하더라도 사용자 정의 초기화 프로그램을 작성하는 이유는 무엇입니까? – vadian

+0

추신 : 그리고 'Int? .self'와 같은 선택적 유형으로 디코딩하는 목적은 무엇입니까? WWDC 비디오는 언급/제안하지 않습니다. – vadian

+0

의견을 보내 주셔서 감사합니다. 커스텀 init은 json이 구조체와 완전히 일치하지 않기 때문에 존재합니다. – Rykuno

답변

0

. 그런 말로하면 불필요한 코드가 많이 생겼습니다. JSON 모델의 모든 것을 코드에 매핑해야하는 것은 아닙니다. Swift 모델에서 속성을 원하지 않는다면 단순히 정의하지 마십시오. 모든 속성 account_idpersonaname를 제외하고, 비 선택적으로 정의와

여기에 짧은 버전입니다 :

시간이 더있는 키 볼 수 documentions를 통과에 소요되는
struct MatchDetail: Decodable { 
    var match_id: Int 
    var barracks_status_dire: Int 
    var barracks_status_radiant: Int 
    var cluster: Int 
    var dire_score: Int 
    var duration: Int 
    var first_blood_time: Int 
    var game_mode: Int 
    var radiant_score: Int 
    var radiant_win: Int 
    var skill: Int 
    var start_time: Int 
    var tower_status_dire: Int 
    var tower_status_radiant: Int 
    var players: [MatchPlayerDetail] 
} 

struct MatchPlayerDetail: Decodable { 
    var match_id: Int 
    var player_slot: Int 
    var ability_upgrades_arr: [Int] 
    var account_id: Int? 
    var assists: Int 
    var backpack_0: Int 
    var backpack_1: Int 
    var backpack_2: Int 
    var deaths: Int 
    var denies: Int 
    var gold: Int 
    var gold_per_min: Int 
    var gold_spent: Int 
    var hero_damage: Int 
    var hero_healing: Int 
    var hero_id: Int 
    var item_0: Int 
    var item_1: Int 
    var item_2: Int 
    var item_3: Int 
    var item_4: Int 
    var item_5: Int 
    var kills: Int 
    var last_hits: Int 
    var leaver_status: Int 
    var level: Int 
    var xp_per_min: Int 
    var personaname: String? 
    var radiant_win: Bool 
    var isRadiant: Bool 
    var win: Int 
    var lose: Int 
    var total_gold: Int 
    var total_xp: Int 
    var kda: Int 
    var abandons: Int 
} 

는 선택 사항이며 어느입니다 아니. Swift는 JSON 디코딩을 합성합니다.