当前位置: 首页 > 面试题库 >

Swift结构:为单个属性处理多种类型

林劲
2023-03-14
问题内容

我正在使用Swift 4并尝试解析一些JSON数据,这些数据显然在某些情况下对于同一键可能具有不同的类型值,例如:

{
    "type": 0.0
}

{
    "type": "12.44591406"
}

我实际上坚持定义我的名字,struct因为我不知道如何处理这种情况,因为

struct ItemRaw: Codable {
    let parentType: String

    enum CodingKeys: String, CodingKey {
        case parentType = "type"
    }
}

抛出"Expected to decode String but found a number instead.",自然,

struct ItemRaw: Codable {
    let parentType: Float

    enum CodingKeys: String, CodingKey {
        case parentType = "type"
    }
}

相应地抛出"Expected to decode Float but found a string/data instead."

定义我时如何处理这种(和类似的)情况struct


问题答案:

尝试对Reddit列表JSON响应上的“已编辑”字段进行解码/编码时遇到了相同的问题。我创建了一个结构,该结构表示给定键可能存在的动态类型。键可以是布尔值或整数。

{ "edited": false }
{ "edited": 123456 }

如果只需要能够解码,则只需实现init(from :)。如果您需要同时使用两种方法,则需要实现encode(to :)函数。

struct Edited: Codable {
    let isEdited: Bool
    let editedTime: Int

    // Where we determine what type the value is
    init(from decoder: Decoder) throws {
        let container =  try decoder.singleValueContainer()

        // Check for a boolean
        do {
            isEdited = try container.decode(Bool.self)
            editedTime = 0
        } catch {
            // Check for an integer
            editedTime = try container.decode(Int.self)
            isEdited = true
        }
    }

    // We need to go back to a dynamic type, so based on the data we have stored, encode to the proper type
    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try isEdited ? container.encode(editedTime) : container.encode(false)
    }
}

在我的Codable类中,然后使用我的结构。

struct Listing: Codable {
    let edited: Edited
}

编辑:针对您的方案的更具体的解决方案

我建议在解码时使用CodingKey协议和一个枚举来存储所有属性。当您创建符合Codable的内容时,编译器将为您创建一个私有枚举CodingKeys。这使您可以根据JSON
Object属性键决定要做什么。

仅举例来说,这就是我正在解码的JSON:

{"type": "1.234"}
{"type": 1.234}

如果您只想将double值从字符串转换为Double,则只需解码字符串,然后从中创建一个double。(这是Itai
Ferber所做的,然后您还必须使用trycoder.decode(type:forKey :)对所有属性进行解码)

struct JSONObjectCasted: Codable {
    let type: Double?

    init(from decoder: Decoder) throws {
        // Decode all fields and store them
        let container = try decoder.container(keyedBy: CodingKeys.self) // The compiler creates coding keys for each property, so as long as the keys are the same as the property names, we don't need to define our own enum.

        // First check for a Double
        do {
            type = try container.decode(Double.self, forKey: .type)

        } catch {
            // The check for a String and then cast it, this will throw if decoding fails
            if let typeValue = Double(try container.decode(String.self, forKey: .type)) {
                type = typeValue
            } else {
                // You may want to throw here if you don't want to default the value(in the case that it you can't have an optional).
                type = nil
            }
        }

        // Perform other decoding for other properties.
    }
}

如果需要将类型和值一起存储,则可以使用符合Codable的枚举而不是struct。然后,您可以仅使用具有JSONObjectCustomEnum的“
type”属性的switch语句,并根据大小写执行操作。

struct JSONObjectCustomEnum: Codable {
    let type: DynamicJSONProperty
}

// Where I can represent all the types that the JSON property can be. 
enum DynamicJSONProperty: Codable {
    case double(Double)
    case string(String)

    init(from decoder: Decoder) throws {
        let container =  try decoder.singleValueContainer()

        // Decode the double
        do {
            let doubleVal = try container.decode(Double.self)
            self = .double(doubleVal)
        } catch DecodingError.typeMismatch {
            // Decode the string
            let stringVal = try container.decode(String.self)
            self = .string(stringVal)
        }
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .double(let value):
            try container.encode(value)
        case .string(let value):
            try container.encode(value)
        }
    }
}


 类似资料:
  • 问题内容: 我有一个班级,想与一个池中的子进程以只读方式共享,所以我准备了一个班级的代理,但是没有用。以下是我的问题的简化示例。 当我运行此代码时,我得到: 看来我无法直接通过代理访问共享库的属性。是使用获取属性的方法的唯一方法,还是我做错了什么? 问题答案: 通常,由其使用的对象及其子类仅公开其引用的对象中的 方法 ,而不公开属性。现在,这里提供,它提供了一个子类,该子类 确实 提供对属性而不是

  • 问题内容: 我尝试解析返回json对象的api。我的问题是,在以下示例中,某些键有时是字符串,有时是类似“ Value”键的对象: 我的结构看起来像这样: 我必须告诉我我完全迷路了(是的,我是个迅速的初学者),我找不到解决我问题的方法。我知道我必须使用自定义init,但我不知道如何。 问题答案: 你可以试试

  • 问题内容: 如果我有一个通用的结构像… 然后我可以扩展为仅当is 时才符合。喜欢… 这可能吗? 我尝试了几种不同的编码方式,但是每种方式都给我一个略有不同的错误。 问题答案: 更新: 条件一致性已在Swift 4.1中实现,您的代码 可以按照Xcode 9.3的要求进行编译和工作。 您正在寻找的是 SE-0143条件符合 (这又是“泛型宣言”的一部分)。该提案已被Swift 4接受,但尚未实施。从

  • execute方法不仅可以执行单条查询语句,而且还可以执行多条查询语句,不同查询语句之间用分号(;)隔开。在给出例子之前,先使用如下SQL建立一个图书销售表t_booksale,并向其中插入三条记录。 建立t_booksale表 DROP TABLE IF EXISTS mydb.t_booksale; CREATE TABLE mydb.t_booksale ( id int(10)

  • 问题内容: swift没有嵌套类吗? 例如,我似乎无法从嵌套类访问主类的属性测试。 问题答案: Swift的嵌套类与Java的嵌套类不同。好吧,它们就像是Java的一种嵌套类,而不是您正在考虑的那种。 在Java中,内部类的实例会自动引用外部类的实例(除非声明了内部类)。如果您有外部类的实例,则只能创建内部类的实例。这就是在Java中您说类似的原因。 在Swift中,内部类的实例独立于外部类的任何

  • 问题内容: 故事: 当您使用解析HTML时,attribute被视为多值属性,并以特殊方式处理: 请记住,单个标签的“ class”属性可以有多个值。当您搜索与某个CSS类匹配的标签时,您将与其任何CSS类进行匹配。 另外,内建的引号用作其他树构建器类的基础,例如: 问题: 如何配置为作为通常的单值属性处理?换句话说,我不希望它专门处理并将其视为常规属性。 我尝试过的 实际上,我通过创建 自定义树