过滤 json 以排除不在枚举大小写中的值
Filter json to exclude value not in enum case
我有一个 API returns
{
"data": [
{
"serviceName": "language",
"date": "2020-01-29 15:00:52"
},
{
"serviceName": "country",
"date": "2020-02-27 10:16:34"
},
{
"serviceName": "currency",
"date": "2020-01-29 15:01:43"
},
{
"serviceName": "category",
"date": "2019-06-27 09:07:11"
},
{
"serviceName": "installationProcedure",
"date": "2020-01-28 12:42:50"
},
{
"serviceName": "remote",
"date": "2021-05-10 10:55:29"
},
{
"serviceName": "accessory",
"date": "2019-09-18 14:48:45"
},
{
"serviceName": "interfaceFirmware",
"date": "2021-05-04 13:57:08"
},
{
"serviceName": "kit",
"date": "2019-09-19 11:37:57"
},
{
"serviceName": "product",
"date": "2020-11-19 15:05:09"
},
{
"serviceName": "installation",
"date": "2021-10-15 18:20:15"
},
{
"serviceName": "controlunit",
"date": "2021-10-13 14:35:55"
},
{
"serviceName": "radioreceiver",
"date": "2021-06-10 10:57:14"
},
{
"serviceName": "firmware",
"date": "2021-05-04 13:57:08"
},
{
"serviceName": "faqs",
"date": "2019-09-18 15:02:05"
},
{
"serviceName": "availableCountriesQuote",
"date": "2019-10-03 09:57:56"
}
]
}
我想在 ServicesList
结构中解码它,我想忽略所有 serviceName
不在我的 Service.Name
枚举中的项目。
我正在使用 Alamofire 调用 API。这样:
let jsonDecoder = JSONDecoder()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
jsonDecoder.dateDecodingStrategy = .formatted(dateFormatter)
authSession.request(ConfigurationRouter.updateServices).validate().responseDecodable(of: ServicesList.self, decoder: jsonDecoder) {
...
}
但我得到一个错误:
"Cannot initialize Name from invalid String value installation"
我怎样才能做到这一点?
struct ServicesList: Codable {
let services: [Service]
}
extension ServicesList {
enum CodingKeys: String, CodingKey {
case services = "data"
}
}
struct Service: Codable {
let name: Name
let date: Date
enum Name: String, Codable {
case language = "language"
case country = "country"
case currency = "currency"
case category = "category"
case installationProcedure = "installationProcedure"
case remote = "remote"
case accessory = "accessory"
case kit = "kit"
case product = "product"
case firmwareFamily = "firmware"
case controlUnitFAQ = "faqs"
case countriesWithEnabledEstimates = "availableCountriesQuote"
case interfaceFirmware = "interfaceFirmware"
}
}
extension Service {
enum CodingKeys: String, CodingKey {
case name = "serviceName"
case date
}
}
只需创建 Name 枚举即可使用 CaseIterable 协议,如下所示:
enum Name: String, Codable, CaseIterable {
case language = "language"
case country = "country"
case currency = "currency"
case category = "category"
case installationProcedure = "installationProcedure"
case remote = "remote"
case accessory = "accessory"
case kit = "kit"
case product = "product"
case firmwareFamily = "firmware"
case controlUnitFAQ = "faqs"
case countriesWithEnabledEstimates = "availableCountriesQuote"
case interfaceFirmware = "interfaceFirmware"
}
然后使用下面的行来过滤服务数组:
services.filter{ Service.Name.allCases.contains( [=11=].name) }
如果要在解码期间执行此操作,则需要分几步完成,我的解决方案首先将数组中的每个项目解码为字典,然后检查是否可以从该字典初始化服务项目。
所以首先我们需要为 Service
定制一个可失败的初始化
init?(name: String?, date: String?) {
guard let date = date else { return nil }
guard let name = name, let serviceName = Name(rawValue: name) else { return nil }
self.name = serviceName
self.date = date //Date conversion needed here, omitted
}
然后我们在 ServiceList
结构
的 init(from:)
中使用它
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
var nestedContainer = try container.nestedUnkeyedContainer(forKey: .services)
var valid = [Service]()
while !nestedContainer.isAtEnd {
let temp = try nestedContainer.decode([String: String].self)
if let service = Service(name: temp["serviceName"], date: temp["date"]) {
valid.append(service)
}
}
services = valid
}
我有一个 API returns
{
"data": [
{
"serviceName": "language",
"date": "2020-01-29 15:00:52"
},
{
"serviceName": "country",
"date": "2020-02-27 10:16:34"
},
{
"serviceName": "currency",
"date": "2020-01-29 15:01:43"
},
{
"serviceName": "category",
"date": "2019-06-27 09:07:11"
},
{
"serviceName": "installationProcedure",
"date": "2020-01-28 12:42:50"
},
{
"serviceName": "remote",
"date": "2021-05-10 10:55:29"
},
{
"serviceName": "accessory",
"date": "2019-09-18 14:48:45"
},
{
"serviceName": "interfaceFirmware",
"date": "2021-05-04 13:57:08"
},
{
"serviceName": "kit",
"date": "2019-09-19 11:37:57"
},
{
"serviceName": "product",
"date": "2020-11-19 15:05:09"
},
{
"serviceName": "installation",
"date": "2021-10-15 18:20:15"
},
{
"serviceName": "controlunit",
"date": "2021-10-13 14:35:55"
},
{
"serviceName": "radioreceiver",
"date": "2021-06-10 10:57:14"
},
{
"serviceName": "firmware",
"date": "2021-05-04 13:57:08"
},
{
"serviceName": "faqs",
"date": "2019-09-18 15:02:05"
},
{
"serviceName": "availableCountriesQuote",
"date": "2019-10-03 09:57:56"
}
]
}
我想在 ServicesList
结构中解码它,我想忽略所有 serviceName
不在我的 Service.Name
枚举中的项目。
我正在使用 Alamofire 调用 API。这样:
let jsonDecoder = JSONDecoder()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
jsonDecoder.dateDecodingStrategy = .formatted(dateFormatter)
authSession.request(ConfigurationRouter.updateServices).validate().responseDecodable(of: ServicesList.self, decoder: jsonDecoder) {
...
}
但我得到一个错误:
"Cannot initialize Name from invalid String value installation"
我怎样才能做到这一点?
struct ServicesList: Codable {
let services: [Service]
}
extension ServicesList {
enum CodingKeys: String, CodingKey {
case services = "data"
}
}
struct Service: Codable {
let name: Name
let date: Date
enum Name: String, Codable {
case language = "language"
case country = "country"
case currency = "currency"
case category = "category"
case installationProcedure = "installationProcedure"
case remote = "remote"
case accessory = "accessory"
case kit = "kit"
case product = "product"
case firmwareFamily = "firmware"
case controlUnitFAQ = "faqs"
case countriesWithEnabledEstimates = "availableCountriesQuote"
case interfaceFirmware = "interfaceFirmware"
}
}
extension Service {
enum CodingKeys: String, CodingKey {
case name = "serviceName"
case date
}
}
只需创建 Name 枚举即可使用 CaseIterable 协议,如下所示:
enum Name: String, Codable, CaseIterable {
case language = "language"
case country = "country"
case currency = "currency"
case category = "category"
case installationProcedure = "installationProcedure"
case remote = "remote"
case accessory = "accessory"
case kit = "kit"
case product = "product"
case firmwareFamily = "firmware"
case controlUnitFAQ = "faqs"
case countriesWithEnabledEstimates = "availableCountriesQuote"
case interfaceFirmware = "interfaceFirmware"
}
然后使用下面的行来过滤服务数组:
services.filter{ Service.Name.allCases.contains( [=11=].name) }
如果要在解码期间执行此操作,则需要分几步完成,我的解决方案首先将数组中的每个项目解码为字典,然后检查是否可以从该字典初始化服务项目。
所以首先我们需要为 Service
init?(name: String?, date: String?) {
guard let date = date else { return nil }
guard let name = name, let serviceName = Name(rawValue: name) else { return nil }
self.name = serviceName
self.date = date //Date conversion needed here, omitted
}
然后我们在 ServiceList
结构
init(from:)
中使用它
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
var nestedContainer = try container.nestedUnkeyedContainer(forKey: .services)
var valid = [Service]()
while !nestedContainer.isAtEnd {
let temp = try nestedContainer.decode([String: String].self)
if let service = Service(name: temp["serviceName"], date: temp["date"]) {
valid.append(service)
}
}
services = valid
}