我具有以下一般结构,其中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
为空{}
仍然显示ImgLike
对象,具有所有nil属性。为什么它不显示数据为零?
然后如果我检查对象?.data == nil,则显示为假!!所以我需要检查每个属性
问题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)
}
}
答案 0 :(得分:1)
Swift-nil
的等效项是JSON-null
和JSON-not-set
。 {}
是JSON中的有效字典,因此不是Swift-nil
。
我想您的意思是,如果您使用自定义解码器功能,则会收到错误消息?这是可以预期的,因为默认的解码器使用decodeIfPresent
而不是decode
来解码可选内容,因为不允许设置这些可选内容。
并且由于您解码了一个空字典{}
,所以没有一个值存在/设置。
{}
解码此CodingKey
结构接受它得到的每个键。
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
}
}
}
答案 1 :(得分:0)
请注意,默认的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?
}
答案 2 :(得分:0)
如上所述,解码器不会将空字典视为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
中的属性甚至可以声明为非可选