【问题标题】:ignore null object in array when parse with Codable swift使用 Codable swift 解析时忽略数组中的空对象
【发布时间】:2019-08-06 14:07:37
【问题描述】:

我正在用 swift Codable 解析这个 API

"total": 7,
"searchResult": [
    null,
    {
        "name": "joe"
        "family": "adam"
    },
    null,
    {
        "name": "martin"
        "family": "lavrix"
    },
    {
        "name": "sarah"
        "family": "mia"
    },
    null,
    {
        "name": "ali"
        "family": "abraham"
    }
]

用这个PaginationModel:

class PaginationModel<T: Codable>: Codable {
    var total: Int?
    var data: T?

    enum CodingKeys: String, CodingKey {
        case total
        case data = "searchResult"
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.total = try container.decodeIfPresent(Int.self, forKey: .total)
        self.data = try container.decodeIfPresent(T.self, forKey: .data)
    }
}

User模特:

struct User: Codable {
    var name: String?
    var family: String?
}

我这样调用jsonDecoder 来解析API json:

let responseObject = try JSONDecoder().decode(PaginationModel<[User?]>.self, from: json)

现在我的问题是 nullsearchResult 数组中。它解析正确,当我访问paginationModel 中的data 时,我在数组中找到了null

解析API时如何忽略所有null,结果将是一个没有任何null的数组

【问题讨论】:

  • @user28434 我更新了我的问题
  • 是的,这个?,它通常被解析为空。我想要一个没有空对象的数组

标签: json swift parsing codable swift4.2


【解决方案1】:

首先,我建议始终将PaginationModel 视为由数组组成。您不必将[User] 作为泛型类型传递,您只需传递User。然后解析器可以使用它解析数组的知识并自动处理null

class PaginationModel<T: Codable>: Codable {
    var total: Int?
    var data: [T]?

    enum CodingKeys: String, CodingKey {
        case total
        case data = "searchResult"
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.total = try container.decodeIfPresent(Int.self, forKey: .total)

        self.data = (try container.decodeIfPresent([T?].self, forKey: .data))?.compactMap { $0 }
    }
}

您可能希望在此处删除可选项并使用一些默认值:

class PaginationModel<T: Codable>: Codable {
    var total: Int = 0
    var data: [T] = []

    enum CodingKeys: String, CodingKey {
        case total
        case data = "searchResult"
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.total = (try container.decodeIfPresent(Int.self, forKey: .total)) ?? 0

        self.data = ((try container.decodeIfPresent([T?].self, forKey: .data)) ?? []).compactMap { $0 }
    }
}

【讨论】:

  • 我还建议将总计(可能)和数据(肯定)设为必需,而不是可选。通过摆脱 decodeIfPresent,这将大大简化这一点。 (同理,name和family真的是Optional吗?“missing”和空字符串有区别吗?)
  • @RobNapier 非常正确。
  • 这正是我想要的。 tnx @Sulthan
  • @RobNapier 我同意你的看法。但是我们的服务器端开发人员说,如果他们突然带有空值(由于服务器的错误或...),您必须处理它
  • 即便如此,您最好将它们设为强制性,并在解码器中将它们默认为空字符串,这样您就不必在程序中的任何地方都将它们视为可选。
【解决方案2】:

简单的解决方案,解码后过滤data

let responseObject = try JSONDecoder().decode(PaginationModel<[User?]>.self, from: data)
responseObject.data = responseObject.data?.filter{$0 != nil}

【讨论】:

  • 这可能是一个解决方案,但我正在寻找更好的解决方案。尤其是在 PaginationModel 中要做的所有事情
  • 问题是泛型T 可以是单个对象也可以是数组。 最佳解决方案是过滤服务器上已有的null 值。
  • @Sajjad, PaginationModel 目前没有关于泛型中使用的实际类型的足够信息,您必须传递您实际正在寻找的信息 Array 然后在 init(from decoder: Decoder)首先将其解码为[T?],然后将compactMap解码为[T]并保存到属性中。
  • 好的。如果我把data : T?改成data: [T?],我能处理吗?
【解决方案3】:

您可以在 decode 中添加数组类型检查:

  required init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.total = try container.decodeIfPresent(Int.self, forKey: .total)
    self.data = try container.decodeIfPresent(T.self, forKey: .data)

    //add the following:
    if let array =  self.data as? Array<Any?> {
        self.data = ( array.compactMap{$0} as? T)
    }
}

【讨论】:

    【解决方案4】:

    注意,您可以将可能为 null/nil 的可解码变量定义为 [Float?](或任何类型),并带有可选的 '?'在数组括号内。

    【讨论】:

      猜你喜欢
      • 2020-01-22
      • 2018-03-30
      • 1970-01-01
      • 1970-01-01
      • 2018-08-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多