Swift Generic不显示nil

时间:2018-09-08 05:47:10

标签: swift codable

我具有以下一般结构,其中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属性。为什么它不显示数据为零?

enter image description here

然后如果我检查对象?.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)

    }
}

3 个答案:

答案 0 :(得分:1)

  1. Swift-nil的等效项是JSON-null和JSON-not-set{}是JSON中的有效字典,因此不是Swift-nil

  2. 我想您的意思是,如果您使用自定义解码器功能,则会收到错误消息?这是可以预期的,因为默认的解码器使用decodeIfPresent而不是decode来解码可选内容,因为不允许设置这些可选内容。
    并且由于您解码了一个空字典{},所以没有一个值存在/设置。

对字典中的键进行计数以避免从JSON-{}解码

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?.dataImgLike???。您正在将可选包装在可选包装中。我看到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中的属性甚至可以声明为非可选