Swift JSON解码动态字符串键时排序丢失问题求助
解决Swift解码动态键JSON时丢失顺序的问题
我明白你遇到的困扰了——Swift标准库的Dictionary是无序集合,解码带动态键的JSON对象时,原始的键顺序会直接丢失。结合你的场景(已经有URL传入的指定顺序列表,或者需要保留JSON本身的顺序),给你几个实用的解决方案:
方法1:利用URL传入的顺序列表(最简单可靠)
既然你已经通过URL拿到了明确的键顺序列表,完全可以先解码成普通字典,再根据这个列表重新构建有序的Devise数组。这种方法不需要依赖JSON的顺序,只要URL传入的列表正确就没问题。
示例代码:
// 假设你从URL参数中拿到的顺序列表 let urlKeyOrder: [String] = ["dynamickey1", "dynamickey2", ...] struct Devise: Codable { let nom: String let EUR: Float let ETH: Float // 映射JSON里的字段到结构体属性(根据你的实际JSON调整) enum CodingKeys: String, CodingKey { case infos1 = "EUR" case infos2 = "ETH" } // 自定义初始化,方便后续设置nom init(nom: String, eur: Float, eth: Float) { self.nom = nom self.EUR = eur self.ETH = eth } // 遵循Codable的默认解码(只处理EUR和ETH) init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.EUR = try container.decode(Float.self, forKey: .infos1) self.ETH = try container.decode(Float.self, forKey: .infos2) self.nom = "" // 后续通过顺序列表设置 } } // 解码逻辑 let jsonData: Data = // 你的JSON数据 let decoder = JSONDecoder() let deviseDict = try decoder.decode([String: Devise].self, from: jsonData) // 根据URL传入的顺序构建有序数组 let orderedDevises = urlKeyOrder.compactMap { key in guard var devise = deviseDict[key] else { // 处理键不存在的情况,比如返回nil跳过 return nil } devise.nom = key return devise }
方法2:用Swift Collections的OrderedDictionary保留JSON原始顺序
如果你需要严格保留JSON本身的键顺序,可以使用苹果官方的Swift Collections库,它提供了OrderedDictionary——一种会保留键插入顺序的字典类型。
步骤:
- 通过Swift Package Manager添加依赖:在你的
Package.swift中加入:
dependencies: [ .package(url: "https://github.com/apple/swift-collections.git", from: "1.0.0") ], targets: [ .target( name: "YourTargetName", dependencies: [.product(name: "Collections", package: "swift-collections")] ) ]
- 解码并转换为有序数组:
import Collections struct Devise: Codable { let nom: String let EUR: Float let ETH: Float enum CodingKeys: String, CodingKey { case infos1 = "EUR" case infos2 = "ETH" } init(nom: String, eur: Float, eth: Float) { self.nom = nom self.EUR = eur self.ETH = eth } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.EUR = try container.decode(Float.self, forKey: .infos1) self.ETH = try container.decode(Float.self, forKey: .infos2) self.nom = "" } } // 解码为OrderedDictionary let decoder = JSONDecoder() let orderedDict = try decoder.decode(OrderedDictionary<String, Devise>.self, from: jsonData) // 转换为带nom的有序数组 let orderedDevises = orderedDict.map { key, var devise in devise.nom = key return devise }
方法3:自定义解码逻辑(无需外部库)
如果不想引入外部库,可以手动实现解码逻辑,遍历JSON的键值对并保留顺序:
struct DataPrix: Codable { let devises: [Devise] init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: DynamicCodingKey.self) var devisesArray = [Devise]() // 遍历所有键,这里的顺序就是JSON的原始顺序 for key in container.allKeys { let nestedContainer = try container.nestedContainer(keyedBy: Devise.CodingKeys.self, forKey: key) let eur = try nestedContainer.decode(Float.self, forKey: .infos1) let eth = try nestedContainer.decode(Float.self, forKey: .infos2) devisesArray.append(Devise(nom: key.stringValue, eur: eur, eth: eth)) } self.devises = devisesArray } // 自定义动态CodingKey,用于匹配JSON的动态键 private struct DynamicCodingKey: CodingKey { let stringValue: String init?(stringValue: String) { self.stringValue = stringValue } let intValue: Int? = nil init?(intValue: Int) { return nil } } } struct Devise { let nom: String let EUR: Float let ETH: Float enum CodingKeys: String, CodingKey { case infos1, infos2 } } // 使用方式 let decoder = JSONDecoder() let dataPrix = try decoder.decode(DataPrix.self, from: jsonData) // dataPrix.devises就是按JSON原始顺序排列的数组
内容的提问来源于stack exchange,提问作者TmSmth
相关产品推荐
相关产品推荐

