【问题标题】:Filter json to exclude value not in enum case过滤 json 以排除不在枚举情况下的值
【发布时间】:2021-10-21 10:47:56
【问题描述】:

我有一个返回的 API

 {
  "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) {
...
}

但我得到一个错误:

“无法从无效的字符串值安装中初始化名称”

我怎样才能做到这一点?

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
    }
}

【问题讨论】:

  • 您可以在您的枚举中添加一个未知案例,以捕获列表中没有的案例stackoverflow.com/a/49697266/2303865
  • 谢谢@LeoDabus,我希望有办法在解码过程中过滤掉所有不需要/未知的Service
  • 我认为您别无选择。如果需要,只需在解码后过滤未知案例即可。

标签: swift enums codable


【解决方案1】:

如果您想在解码期间执行此操作,则需要分几个步骤完成,我的解决方案首先将数组中的每个项目解码为字典,然后检查是否可以从该字典初始化 Service 项。

所以首先我们需要一个针对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
}

【讨论】:

    【解决方案2】:

    只需让 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( $0.name) }
    

    【讨论】:

    • 对不起@Moayad Al kouz,我忘了写我使用Alamofire responseDecodable来解码json,我在那里得到一个错误。我编辑了我的问题。
    猜你喜欢
    • 1970-01-01
    • 2020-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-18
    相关资源
    最近更新 更多