我有以下一般结构,其中data
可以是任何其他可编码对象
struct GeneralResponse<T:Codable>: Codable {
let message: String
let status: Bool
let data: T?
enum CodingKeys: String, CodingKey {
case message = "Message"
case status = "Status"
case data = "Data"
}
}
我有跟随响应可编码类,将在data
用作GeneralResponse
class ImgLike: Codable {
let id: Int?
let imageID, user: String?
@available(*, deprecated, message: "Do not use.")
private init() {
fatalError("Swift 4.1")
}
enum CodingKeys: String, CodingKey {
case id = "ID"
case imageID = "ImageID"
case user = "User"
}
}
问题1:当令牌在API上过期时,响应data
为空{}
仍然显示具有所有nil属性的ImgLike
对象。为什么它不显示数据为零?
然后如果我检查对象?.data == nil它显示为false !!所以我需要检查每个属性
问题2:在ImgLike如果我使用自定义编码功能。 GeneralResponse
未解析ImgLike
未解析它在catch语句中显示错误
required init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
imageID = try values.decode(String.self, forKey: .imageID)
user = try values.decode(String.self, forKey: .user)
do {
id = Int(try values.decode(String.self, forKey: .id))
} catch {
id = try values.decode(Int.self, forKey: .id)
}
}
nil
的等价物是JSON-null
和JSON-not-set
。 {}
是JSON中的有效词典,因此不是Swift-nil
。decodeIfPresent
而不是decode
来解码选项,因为它们不允许被设置。
因为你解码一个空字典{}
没有任何值存在/设置。{}
这个CodingKey
-struct接受它获得的每个密钥。
fileprivate struct AllKeysAllowed: CodingKey {
var stringValue: String
var intValue: Int?
init?(stringValue: String) {
self.stringValue = stringValue
}
init?(intValue: Int) {
self.intValue = intValue
stringValue = "\(intValue)"
}
}
struct GeneralResponse<T:Codable>: Decodable {
let message: String
let status: Bool
let data: T?
enum CodingKeys: String, CodingKey {
case message = "Message"
case status = "Status"
case data = "Data"
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
message = try container.decode(String.self, forKey: .message)
status = try container.decode(Bool.self, forKey: .status)
将.data
解码为一个容纳了所有密钥的容器。
然后使用JSON-dictionary
可以读取dataContainer.allKeys.count
中的键数。
let dataContainer = try container.nestedContainer(keyedBy: AllKeysAllowed.self, forKey: .data)
if dataContainer.allKeys.count != 0 {
data = try container.decode(T.self, forKey: .data)
} else {
data = nil
}
}
}
请注意,默认的Codable
实现使用decodeIfPresent
而不是decode
。即使密钥不存在于JSON中,decodeIfPresent
也不会抛出错误。它只会返回nil
。因此,空JSON字典没有KVP,因此所有属性都设置为nil
。
在Codable
的自定义实现中,您使用的是decode
,如果找不到密钥则会抛出错误。
object?.data != nil
之所以是因为object?.data
是ImgLike???
。您将可选项包装在可选项中的可选项中。我看到object
的类型是GeneralResponse<ImgLike?>?
。这将使data
的类型为ImgLike??
。我不认为这是你的意图。你可能打算使用GeneralRepsonse<ImgLike>
。您可能忘记在某处打开可选项。您还需要打开最外层的可选项:
if let nonNilObject = object {
// nonNilObject.data is of type ImgLike?
}
如前所述,解码器不会将空字典视为nil
。
您可以使用微小协议和KeyedDecodingContainer
的扩展以通用方式添加此功能
public protocol EmptyDictionaryRepresentable {
associatedtype CodingKeys : RawRepresentable where CodingKeys.RawValue == String
associatedtype CodingKeyType: CodingKey = Self.CodingKeys
}
extension KeyedDecodingContainer {
public func decodeIfPresent<T>(_ type: T.Type, forKey key: KeyedDecodingContainer.Key) throws -> T?
where T : Decodable & EmptyDictionaryRepresentable
{
guard contains(key) else { return nil }
let container = try nestedContainer(keyedBy: type.CodingKeyType.self, forKey: key)
return container.allKeys.isEmpty ? nil : try decode(T.self, forKey: key)
}
}
只需将EmptyDictionaryRepresentable
符合性添加到ImgLike
,即可推断出相关类型。
class ImgLike: Codable, EmptyDictionaryRepresentable {
ImgLike
中的属性甚至可以声明为非可选属性