Swift Generic不显示nil

问题描述 投票:0回答:3

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

enter image description here

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

    }
}
swift codable
3个回答
1
投票
  1. Swift-nil的等价物是JSON-null和JSON-not-set{}是JSON中的有效词典,因此不是Swift-nil
  2. 我猜你的意思是你使用自定义解码器功能会出错?这是预期的,因为默认解码器使用decodeIfPresent而不是decode来解码选项,因为它们不允许被设置。 因为你解码一个空字典{}没有任何值存在/设置。

Counting keys in dict to avoid decoding from JSON-{}

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

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

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中的属性甚至可以声明为非可选属性

© www.soinside.com 2019 - 2024. All rights reserved.