且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Swift Codable-解析JSON数组,可以包含不同的数据类型

更新时间:2022-05-17 00:06:28

我使用了 quicktype 推断config_data的类型,它建议为您的对象,字符串和整数值使用单独的大小写的枚举:

I used quicktype to infer the type of config_data and it suggested an enum with separate cases for your object, string, and integer values:

struct ConfigData {
    let configData: [ConfigDatumElement]
}

enum ConfigDatumElement {
    case configDatumClass(ConfigDatumClass)
    case integer(Int)
    case string(String)
}

struct ConfigDatumClass {
    let name, configTitle: String
}

这是完整的代码示例.解码enum有点棘手,但是quicktype可以帮助您解决问题:

Here's the complete code example. It's a bit tricky to decode the enum but quicktype helps you out there:

// To parse the JSON, add this file to your project and do:
//
//   let configData = try? JSONDecoder().decode(ConfigData.self, from: jsonData)

import Foundation

struct ConfigData: Codable {
    let configData: [ConfigDatumElement]

    enum CodingKeys: String, CodingKey {
        case configData = "config_data"
    }
}

enum ConfigDatumElement: Codable {
    case configDatumClass(ConfigDatumClass)
    case integer(Int)
    case string(String)

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let x = try? container.decode(Int.self) {
            self = .integer(x)
            return
        }
        if let x = try? container.decode(String.self) {
            self = .string(x)
            return
        }
        if let x = try? container.decode(ConfigDatumClass.self) {
            self = .configDatumClass(x)
            return
        }
        throw DecodingError.typeMismatch(ConfigDatumElement.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for ConfigDatumElement"))
    }

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

struct ConfigDatumClass: Codable {
    let name, configTitle: String

    enum CodingKeys: String, CodingKey {
        case name
        case configTitle = "config_title"
    }
}

使用enum很高兴,因为这样可以得到最大的类型安全性.其他答案似乎都没有.

It's nice to use the enum because you get the most type-safety that way. The other answers seem to lose this.

使用quicktype的便捷初始化程序选项,一个有效的代码示例为:

Using quicktype's convenience initializers option, a working code sample is:

let data = try ConfigData("""
{
  "config_data": [
    {
      "name": "illuminate",
      "config_title": "Blink"
    },
    {
      "name": "shoot",
      "config_title": "Fire"
    },
    "illuminate",
    "shoot",
    25,
    100
  ]
}
""")

for item in data.configData {
    switch item {
    case .configDatumClass(let d):
        print("It's a class:", d)
    case .integer(let i):
        print("It's an int:", i)
    case .string(let s):
        print("It's a string:", s)
    }
}

此打印:

It's a class: ConfigDatumClass(name: "illuminate", configTitle: "Blink")
It's a class: ConfigDatumClass(name: "shoot", configTitle: "Fire")
It's a string: illuminate
It's a string: shoot
It's an int: 25
It's an int: 100