提问人:kbartlett 提问时间:11/16/2023 更新时间:11/16/2023 访问量:57
Swift 将 JSON 从 [String: Int] 解码为元组或自定义类型
Swift decoding JSON from [String: Int] into tuple or custom type
问:
我正在编写一个应用程序来计算女神异闻录 5 融合(如果您不熟悉游戏,请考虑口袋妖怪,但使用世界神话生物而不是口袋怪物)。
我将应用程序的数据本地存储在我从 github 中其他人的项目获取的 json 文件中。但是由于这种结构,我在解码对象时遇到了问题:
[
{
"name": "Abaddon",
"inherits": "Curse",
"item": "Makarakarn",
"skillCard": true,
"arcana": "Judgement",
"level": 74,
"stats": {
"strength": 51,
"magic": 38,
"endurance": 58,
"agility": 43,
"luck": 39
},
"elements": {
"physical": "ab",
"gun": "ab",
"fire": "-",
"ice": "-",
"electric": "-",
"wind": "-",
"psychic": "wk",
"nuclear": "wk",
"bless": "-",
"curse": "ab"
},
"skills": {
"Absorb Phys": 79,
"Deathbound": 0,
"Gigantomachia": 80,
"Makarakarn": 0,
"Spirit Drain": 0,
"Survival Trick": 77
}
}
]
具体来说,底部的技能不是我希望的格式。有没有办法将它们解码为像这样的元组或自定义结构?(String, Int)
struct PersonaSkill: Codable, Hashable {
let skill: String
let level: Int
}
由于我在本地存储了数据,因此我可以手动更改结构,但是有 ~400 个条目需要更新,因此需要相当长的时间并且非常容易出错,所以我宁愿不要这样做。
以下是 Persona 的完整模型:
import Foundation
import SwiftUI
struct Fusion: Codable {
let sources: [Persona]
let result: Persona
let cost: Int
}
struct Persona: Codable {
let name: String
let special: Bool?
let inherits: String?
let item: String
let itemR: String?
let skillCard: Bool?
let arcana: ArcanaType
let level: Int
let stats: Stats
let elements: ElementReactions
let skills: [PersonaSkill]?
let personality: PersonalityType?
let mementosArea: [AreaType]?
let floor: String?
let trait: String?
let rare: Bool?
let dlc: Bool?
}
struct Stats: Codable {
let strength: Int
let magic: Int
let endurance: Int
let agility: Int
let luck: Int
}
struct PersonaSkill: Codable, Hashable {
let skill: String
let level: Int
}
struct ElementReactions: Codable {
let physical: ReactionType
let gun: ReactionType
let fire: ReactionType
let ice: ReactionType
let electric: ReactionType
let wind: ReactionType
let psychic: ReactionType
let nuclear: ReactionType
let bless: ReactionType
let curse: ReactionType
}
enum ElementType: String, Codable {
case physical, gun, fire, ice, electric, wind, nuclear, psychic, bless, curse
}
enum ReactionType: String, Codable {
case weak = "wk", neutral = "-", resist = "rs", null = "nu", repel = "rp", absorb = "ab"
var description: String {
switch self {
case .weak:
"Weak"
case .neutral:
"-"
case .resist:
"Resist"
case .null:
"Null"
case .repel:
"Repel"
case .absorb:
"Absorb"
}
}
var colorName: Color {
switch self {
case .weak:
Color(.red)
case .neutral:
Color(.black)
case .resist:
Color(.blue)
case .null:
Color(.gray)
case .repel:
Color(.yellow)
case .absorb:
Color(.green)
}
}
}
enum AreaType: String, Codable {
case Aiyatsbus, Akzeriyyuth, Adyeshach, Chemdah, Daat = "Da'at", Kaitul, Sheriruth, Qimranut
}
enum ArcanaType: String, Codable {
case chariot = "Chariot"
case death = "Death"
case devil = "Devil"
case emperor = "Emperor"
case empress = "Empress"
case faith = "Faith"
case fool = "Fool"
case fortune = "Fortune"
case hangedMan = "Hanged Man"
case hermit = "Hermit"
case hierophant = "Hierophant"
case judgement = "Judgement"
case justice = "Justice"
case lovers = "Lovers"
case magician = "Magician"
case moon = "Moon"
case priestess = "Priestess"
case sun = "Sun"
case star = "Star"
case strength = "Strength"
case temperance = "Temperance"
case tower = "Tower"
case world = "World"
}
enum PersonalityType: String, Codable {
case gloomy = "Gloomy"
case irritable = "Irritable"
case timid = "Timid"
case upbeat = "Upbeat"
case unknown = "Unknown"
}
这是我用来解码 JSON 的代码:
import Foundation
import UIKit
class AppModel: ObservableObject {
@Published var royal: Bool = false {
didSet {
loadData()
}
}
@Published var personae: [Persona]
@Published var skills: [Skill]
@Published var items: [Item]
init() {
self.royal = false
self.personae = Bundle.main.decode([Persona].self, from: "PersonaTestData.json")
self.skills = Bundle.main.decode([Skill].self, from: "SkillTestData.json")
self.items = Bundle.main.decode([Item].self, from: "ItemData.json")
}
func loadData() {
if self.royal {
self.personae = Bundle.main.decode([Persona].self, from: "PersonaDataRoyal.json")
self.skills = Bundle.main.decode([Skill].self, from: "SkillDataRoyal.json")
self.items = Bundle.main.decode([Item].self, from: "ItemDataRoyal.json")
} else {
self.personae = Bundle.main.decode([Persona].self, from: "PersonaData.json")
self.skills = Bundle.main.decode([Skill].self, from: "SkillData.json")
self.items = Bundle.main.decode([Item].self, from: "ItemData.json")
}
}
}
extension Bundle {
func decode<T: Decodable>(_ type: T.Type, from file: String) -> T {
guard let url = self.url(forResource: file, withExtension: nil) else {
fatalError("Failed to locate \(file)")
}
guard let data = try? Data(contentsOf: url) else {
fatalError("Failed to load \(file)")
}
let decoder = JSONDecoder()
guard let loaded = try? decoder.decode(T.self, from: data) else {
fatalError("Failed to decode \(file)")
}
return loaded
}
}
答:
0赞
Sweeper
11/16/2023
#1
如果要避免手动解码 的所有其他属性,可以编写自己的类型来包装 .Persona
SkillsCollection
[PersonaSkill]
struct PersonaSkill: Hashable {
let skill: String
let level: Int
}
struct SkillsCollection: Codable {
let contents: [PersonaSkill]
init(from decoder: Decoder) throws {
let dictionary = try [String: Int](from: decoder)
contents = dictionary.map { PersonaSkill(skill: $0, level: $1) }
}
func encode(to encoder: Encoder) throws {
let dictionary = Dictionary(uniqueKeysWithValues: contents.map { ($0.skill, $0.level) })
try dictionary.encode(to: encoder)
}
}
在 中,您将拥有以下类型的属性:Persona
SkillsCollection?
// you would need to do skills?.contents to access the actual array
let skills: SkillsCollection?
请注意,不会保留技能的顺序,因为这是一个 JSON 字典。
评论
0赞
kbartlett
11/17/2023
太棒了,谢谢!我可以稍后处理它们,所以顺序没什么大不了的。
评论
map