Учитывая то, что вы описали, вы должны хранить параметры в виде перечисления, например:
enum Param: CustomStringConvertible {
case string(String)
case int(Int)
case array([Param])
var description: String {
switch self {
case let .string(string): return string
case let .int(int): return "\(int)"
case let .array(array): return "\(array)"
}
}
}
Параметр может быть строкой, целым числом или массивом других параметров.
Затем вы можете сделать Param Decodable, пробуя каждый вариант по очереди:
extension Param: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let string = try? container.decode(String.self) {
self = .string(string)
} else if let int = try? container.decode(Int.self) {
self = .int(int)
} else {
self = .array(try container.decode([Param].self))
}
}
}
Учитывая это, в LMSRequest нет необходимости в настраиваемой логике декодирования:
struct LMSRequest: Decodable {
let id : Int?
let method : String?
let params : [Param]?
}
В качестве примечания, я бы внимательно рассмотрел, действительно ли все эти поля являются необязательными. Очень удивительно, что id
является необязательным, и довольно удивительно, что method
является необязательным, и немного удивительно, что params
являются необязательными. Если они на самом деле не являются необязательными, не делайте их необязательными в типе.
Судя по вашим комментариям, вы, вероятно, не понимаете, как получить доступ к перечислениям. params[1]
не является [Param]
. Это .array([Param])
. Таким образом, вы должны сопоставить его с шаблоном, поскольку это могла быть строка или int.
if case let .array(values) = lms.params[1] { print(values[0]) }
Тем не менее, если вы делаете это часто, вы можете упростить это с помощью расширений в Param:
extension Param {
var stringValue: String? { if case let .string(value) = self { return value } else { return nil } }
var intValue: Int? { if case let .int(value) = self { return value } else { return nil } }
var arrayValue: [Param]? { if case let .array(value) = self { return value } else { return nil } }
subscript(_ index: Int) -> Param? {
return arrayValue?[index]
}
}
При этом вы можете говорить что-то вроде:
let serverstatus: String? = lms.params[1][0]?.stringValue
Что, вероятно, ближе к тому, что вы имели в виду. (: String?
просто указывает на возвращаемый тип; это не обязательно.)
Более сложный и проработанный пример этого подхода см. В моем общем JSON Decodable, является подмножеством.
person
Rob Napier
schedule
10.06.2018
params
- это[String]?
, но, как вы заметили, он включает как строку, так и вложенный массив строк. Как вы хотите, чтобы он выглядел в итогеparams
? Должен ли он их сплющить или что-то еще? Также есть1
в середине вложенного массива данных, который не является строкой. Вы хотите, чтобы это преобразовалось в String"1"
или что-то еще? - person Rob Napier   schedule 10.06.2018Cat
объектов?House
объектов? Вы действительно имеете в виду абсолютно любой объект, даже тот, который не может быть выражен в JSON? Или вы имеете в виду небольшой список типов, вероятно, только строки и целые числа, и в этом случае вы имеете в виду перечисление. Или, глядя на это, я подозреваю, что это действительно все строки и требуется преобразование. Но сначала вам нужно определить свой тип Swift, а затем мы сможем его декодировать. - person Rob Napier   schedule 10.06.2018