在Swift中将JSON双精度浮点数解码为CGFloat

p5cysglq  于 2023-02-06  发布在  Swift
关注(0)|答案(1)|浏览(152)

编辑:代码工作,问题是与导入的数据集,它没有将空字段分类为数字,而是作为字符串代替。
我的应用程序需要从JSON文件导入类似结构的值,如下所示

  1. [
  2. {
  3. "id": 1,
  4. "string": "Text String",
  5. "int": 6,
  6. "cgfloat": 1.1,
  7. }
  8. ]

进口:

  1. id = try container.decode(Int.self, forKey: .id)
  2. string = try container.decode(String.self, forKey: .string)
  3. int = try container.decode(Int.self, forKey: .int)

我不知道如何读取值1.1,Double、Float、CGFloat、Int和String在这里都不起作用。我是否需要更改数据结构,或者是否有办法在Swift中正确解释该值?
这会使应用程序崩溃:

  1. let cgfloat = try container.decode(CGFloat.self, forKey: . cgfloat)

根据要求的完整代码:

  1. struct Monster: Codable, Comparable {
  2. enum MonsterStatus: String, Identifiable, Codable, Hashable {
  3. var id: Self {
  4. return self
  5. }
  6. case Normal = "Normal"
  7. case Attack = "Attack"
  8. case Hit = "Hit"
  9. case Defend = "Defend"
  10. case Dead = "Dead"
  11. }
  12. enum MonsterType: String, Identifiable, Codable, Hashable {
  13. var id: Self {
  14. return self
  15. }
  16. case Undead = "Undead"
  17. case Human = "Human"
  18. case Dinosaur = "Dinosaur"
  19. case Dwarf = "Dwarf"
  20. case Elf = "Elf"
  21. case Wisp = "Wisp"
  22. case Ghost = "Ghost"
  23. case Beast = "Beast"
  24. case Snake = "Snake"
  25. case Giant = "Giant"
  26. case Demon = "Demon"
  27. case Dragon = "Dragon"
  28. case Error = "Error"
  29. }
  30. struct ImagesCatalog: Equatable, Codable {
  31. var Normal: String
  32. var Attack: String
  33. var Defend: String
  34. var Hit: String
  35. var Dead: String
  36. }
  37. static func < (lhs: Monster, rhs: Monster) -> Bool {
  38. return lhs.id < rhs.id
  39. }
  40. static func == (lhs: Monster, rhs: Monster) -> Bool {
  41. return lhs.id == rhs.id
  42. }
  43. var id: Int
  44. let name: String
  45. let image: String = ""
  46. var imagePrefix:String = ""
  47. var strength: Int = 4
  48. var life: Int = 1
  49. var fleeChance: Int = 0
  50. var isBoss: Bool = false
  51. var flying: Bool = false
  52. var mage: Bool = false
  53. var venomous: Bool = false
  54. var giant: Bool = false
  55. var carnivore: Bool = false
  56. var herbivore: Bool = false
  57. var type: MonsterType
  58. var location: HordeGameData.Location
  59. var isFaceUp: Bool = false
  60. var soundAppear: String = "skeletonWalk4.mp3"
  61. var soundHit: String = "skeletonHit1.mp3"
  62. var soundDefend: String = "skeletonEmerge1.mp3"
  63. var soundAttack: String = "skeletonHit4.mp3"
  64. var soundDead: String = "skeletonShatter1.mp3"
  65. var playSound: Bool = true
  66. let images: ImagesCatalog
  67. var imagePic: String
  68. var scaleFactor: CGFloat = 0.5
  69. var active: Bool = false
  70. var slash: Bool = false
  71. var description: String = ""
  72. var status: MonsterStatus = .Normal
  73. func returnColor() -> Color {
  74. if isFaceUp && isBoss {
  75. return .red
  76. } else {
  77. return .red
  78. }
  79. }
  80. func provideID() -> String {
  81. return String(self.id)
  82. }
  83. mutating func playSoundToggle() {
  84. self.playSound.toggle()
  85. }
  86. mutating func reduceStrengthBy(_ amount: Int) {
  87. if strength > amount {
  88. self.strength -= amount
  89. }
  90. }
  91. mutating func defineImage(status: MonsterStatus, slash: Bool) {
  92. self.status = status
  93. switch status {
  94. case .Normal: imagePic = images.Normal
  95. if playSound {
  96. Sound.play(file: self.soundAppear)
  97. }
  98. case .Attack: imagePic = images.Attack
  99. if playSound {
  100. Sound.play(file: self.soundAttack)
  101. }
  102. case .Defend: imagePic = images.Defend
  103. if playSound {
  104. Sound.play(file: self.soundDefend)
  105. }
  106. case .Hit : imagePic = images.Hit
  107. if playSound {
  108. Sound.play(file: self.soundHit)
  109. }
  110. case .Dead : imagePic = images.Dead
  111. if playSound {
  112. Sound.play(file: self.soundDead)
  113. }
  114. }
  115. self.slash = slash
  116. }
  117. mutating func nextImage() {
  118. switch self.status {
  119. case .Normal: defineImage(status: .Attack, slash: false)
  120. case .Attack: defineImage(status: .Defend, slash: false)
  121. case .Defend: defineImage(status: .Hit, slash: false)
  122. case .Hit : defineImage(status: .Dead, slash: false)
  123. case .Dead : defineImage(status: .Normal, slash: false)
  124. }
  125. }
  126. mutating func setID(_ id: Int) {
  127. self.id = id
  128. }
  129. mutating func reduceLife() {
  130. life -= 1
  131. }
  132. mutating func addLife() {
  133. life += 1
  134. }
  135. func provideLife() -> String {
  136. var lifeString = ""
  137. for _ in 0..<life {
  138. lifeString.append("💜")
  139. }
  140. return lifeString
  141. }
  142. ///
  143. private enum CodingKeys: String, CodingKey {
  144. case id
  145. case name
  146. case imagePrefix
  147. case strength
  148. case life
  149. case fleeChance
  150. case isBoss
  151. case flying
  152. case mage
  153. case venomous
  154. case giant
  155. case carnivore
  156. case herbivore
  157. case type
  158. case location
  159. case soundAppear
  160. case soundHit
  161. case soundDefend
  162. case soundAttack
  163. case soundDead
  164. case images
  165. case scaleFactor
  166. case description
  167. }
  168. init(from decoder: Decoder) throws {
  169. let container = try decoder.container(keyedBy: CodingKeys.self)
  170. id = try container.decode(Int.self, forKey: .id)
  171. name = try container.decode(String.self, forKey: .name)
  172. imagePrefix = try container.decode(String.self, forKey: .imagePrefix)
  173. strength = try container.decode(Int.self, forKey: .strength)
  174. life = try container.decode(Int.self, forKey: .life)
  175. fleeChance = try container.decode(Int.self, forKey: .fleeChance)
  176. isBoss = try container.decode(Bool.self, forKey: .isBoss)
  177. flying = try container.decode(Bool.self, forKey: .flying)
  178. mage = try container.decode(Bool.self, forKey: .mage)
  179. venomous = try container.decode(Bool.self, forKey: .venomous)
  180. giant = try container.decode(Bool.self, forKey: .giant)
  181. carnivore = try container.decode(Bool.self, forKey: .carnivore)
  182. herbivore = try container.decode(Bool.self, forKey: .herbivore)
  183. let monsterTypeValue = try container.decode(String.self, forKey: .type)
  184. type = MonsterType(rawValue: monsterTypeValue) ?? .Error
  185. let locationValue = try container.decode(String.self, forKey: .location)
  186. location = HordeGameData.Location(rawValue: locationValue) ?? .Error
  187. soundAppear = try container.decode(String.self, forKey: .soundAppear)
  188. soundHit = try container.decode(String.self, forKey: .soundHit)
  189. soundDefend = try container.decode(String.self, forKey: .soundDefend)
  190. soundAttack = try container.decode(String.self, forKey: .soundAttack)
  191. soundDead = try container.decode(String.self, forKey: .soundDead)
  192. images = .init(Normal: imagePrefix + "Normal",
  193. Attack: imagePrefix + "Attack",
  194. Defend: imagePrefix + "Defend",
  195. Hit: imagePrefix + "Hit",
  196. Dead: imagePrefix + "Dead")
  197. imagePic = imagePrefix + "Normal"
  198. // let stringScale = try container.decode(CGFloat.self, forKey: .scaleFactor)
  199. // print(stringScale)
  200. // scaleFactor = stringScale.CGFloatValue() ?? 0.5
  201. description = try container.decode(String.self, forKey: .description)
  202. print("Monster \(id): \(name) imported")
  203. }
  204. }

解码功能

  1. class MonsterInventory {
  2. static let shared = MonsterInventory()
  3. var staticItems: [Monster] = []
  4. private init() {
  5. self.parseMonsterJson()
  6. }
  7. private func parseMonsterJson() {
  8. guard let monsterJsonFileUrl = Bundle.main.url(forResource: "Monsters", withExtension: "json") else {
  9. fatalError("Unable to find file")
  10. }
  11. do {
  12. let content = try Data(contentsOf: monsterJsonFileUrl)
  13. let jsonDecoder = JSONDecoder()
  14. staticItems = try jsonDecoder.decode([Monster].self, from: content)
  15. } catch let error {
  16. print(error.localizedDescription)
  17. staticItems = []
  18. }
  19. }
  20. func fetchMonsterWithID(_ id: Int) -> Monster {
  21. return staticItems.filter{$0.id == id}.first ?? staticItems.filter{$0.id == 10}.first!
  22. }
  23. func fetchMonsterWithName(_ name: String) -> Monster {
  24. return staticItems.filter{$0.name == name}.first ?? staticItems.filter{$0.id == 10}.first!
  25. }
  26. }

下面是JSON的一个摘录:

  1. [
  2. {
  3. "id": 1,
  4. "name": "Skeleton Warrior",
  5. "strength": 6,
  6. "life": 1,
  7. "fleeChance": 0,
  8. "isBoss": false,
  9. "flying": false,
  10. "mage": false,
  11. "venomous": false,
  12. "giant": false,
  13. "carnivore": false,
  14. "herbivore": false,
  15. "type": "Undead",
  16. "location": "Plains",
  17. "soundAppear": "skeletonWalk4.mp4",
  18. "soundHit": "skeletonHit1.mp3",
  19. "soundDefend": "swordSwoosh2.m4a",
  20. "soundAttack": "swordSwoosh1.m4a",
  21. "soundDead": "skeletonShatter2.mp3",
  22. "imagePrefix": "SkeletonWarrior",
  23. "imageNormal": "SkeletonWarriorNormal",
  24. "imageAttack": "SkeletonWarriorAttack",
  25. "imageDefend": "SkeletonWarriorDefend",
  26. "imageHit": "SkeletonWarriorHit",
  27. "imageDead": "SkeletonWarriorDead",
  28. "scaleFactor": 1.1,
  29. "description": "A weak skeleton fighter."
  30. },
  31. ...
  32. ]
p8h8hvxi

p8h8hvxi1#

我不确定为什么要逐个密钥地解码...我认为您应该创建一个struct,除非您真的需要逐个密钥地解码(例如
侧翼提到的评论这个答案)。
如果您确实需要逐个键进行解码,则需要提供有关如何解码JSON的更多详细信息
这是适合JSON的结构体

  1. struct Elm: Codable {
  2. var id: Int
  3. var string: String
  4. var int: Int
  5. var cgfloat: CGFloat // also can be Double and Float
  6. }

您还可以更改变量名称以遵循camelCase命名。

  1. struct Elm: Codable {
  2. var id: Int
  3. var string: String
  4. var int: Int
  5. var cgFloat: Double
  6. private enum CodingKeys : String, CodingKey {
  7. case id, string, int, cgFloat = "cgfloat"
  8. }
  9. }

这就是解码对象的方法。

  1. let decoder = JSONDecoder()
  2. let obj = try! decoder.decode([Elm].self, from: json.data(using: .utf8)!)

在本例中,JSON的根是一个数组,因此在这种情况下,我们不使用Elm.self,而是使用[Elm].self解码到数组
这样Swift将JSON KeyMap到匹配的变量名(或者使用编码键,就像我在第二个结构体中演示的那样)
这在Playground进行了测试

    • 更新1**

根据您更新的问题,我现在明白了为什么个别解码,但假设您有问题的关键是scaleFactor,这是唯一的浮点数在样本中,您提供的代码工作没有任何问题。

  1. // [...]
  2. // uncommented your code
  3. scaleFactor = try container.decode(CGFloat.self, forKey: .scaleFactor)
  4. let _scaleFactor = try container.decode(CGFloat.self, forKey: .scaleFactor) // both work
  5. description = try container.decode(String.self, forKey: .description)
  6. print("Monster \(id): \(name) imported")

现在您可以仔细检查文件的读取是否正确(如果您可以读取文件的其余部分,则可能正确)

展开查看全部

相关问题