#json #swift #encodable
#json #swift #кодируемый
Вопрос:
В моем проекте iOS Swift мне нужно преобразовать объекты в JSON.
У меня есть один простой класс:
class Car : Encodable
{
var brand: String
init(brand: String)
{
self.brand = brand
}
}
и один подкласс:
class SUVCar : Car
{
var weight: Int
init(_ weight: Int)
{
self.weight = weight
super.init(brand: "MyBrand")
}
}
Я использую следующую общую функцию для преобразования объектов и массивов в JSON:
func toJSON<T : Encodable>(_ object: T) -> String?
{
do
{
let jsonEncoder = JSONEncoder()
let jsonEncode = try jsonEncoder.encode(object)
return String(data: jsonEncode, encoding: .utf8)
}
catch
{
return nil
}
}
Теперь предположим, что я хочу преобразовать следующую переменную в JSON:
var arrayOfCars: Array<Car> = []
arrayOfCars.append(SUVCar(1700))
arrayOfCars.append(SUVCar(1650))
Я использую Array<Car>
в качестве типа для этого массива, потому что в этом массиве есть другие типы автомобилей. Я просто упростил его здесь для удобства чтения.
Итак, вот что я сделал:
let json = toJSON(arrayOfCars)
Но по какой-то причине при преобразовании в JSON weight
атрибут of SUVCar
игнорируется, даже если arrayOfCars
содержит SUVCar
объекты, и я получаю JSON, который выглядит следующим образом:
[{brand: "MyBrand"}, {brand: "MyBrand"}]
Итак, как я могу получить weight
атрибут SUVCar
в моем JSON? Что я пропустил?
Спасибо.
Комментарии:
1. Вы анализируете декодирование массива
Car
s, почему вы ожидаете, что он будет декодировать массивSUVCar
s? Измените объявление массива наvar arrayOfCars: [SUVCar] = []
2. @LeoDabus Ну, потому что в Kotlin это работает как шарм, поэтому я ожидал подобного поведения
Ответ №1:
class SUVCar: Car
{
enum SUVCarKeys: CodingKey {
case weight
}
var weight: Int
init(_ weight: Int)
{
self.weight = weight
super.init(brand: "MyBrand")
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: SUVCarKeys.self)
try container.encode(weight, forKey: .weight)
try super.encode(to: encoder)
}
}
Если вы увеличите реализацию encode для подкласса, вы сможете добавить дополнительные свойства
Комментарии:
1. Спасибо, я попробую это. Но я поражен количеством кода, который мне нужно добавить, в то время как в Kotlin и используя библиотеку Gson, я могу сделать то же самое в одной единственной строке кода…
Ответ №2:
Вместо того, чтобы настраивать все ваши подклассы для правильного кодирования, вы могли бы решить эту проблему, введя тип, который содержит ваши разные типы автомобилей
struct CarCollection: Encodable {
let suvs: [SUVCar]
let jeeps: [Jeep]
let sedans: [Sedan]
}
(предполагая два других подкласса class Jeep: Car {}
и class Sedan: Car {}
)
Тогда вам не понадобится дополнительный код, и кодирование будет простым
let cars = CarCollection(suvs: [SUVCar(brand: "x", weight: 1000)],
jeeps: [Jeep(brand: "y")],
sedans: [Sedan(brand: "z")])
if let json = toJSON(cars) {
print(json)
}
{«джипы»: [{«бренд»:»x»}], «внедорожники»: [{«бренд»:»x»}], «седаны»: [{«бренд»: «a»}]}
Немного не по теме, но struct может быть лучшим выбором, чем class здесь, или, по крайней мере, это был бы рекомендуемый выбор, поэтому вот как это будет выглядеть со struct и протоколом вместо суперкласса. Приведенный выше код останется тем же.
protocol Car : Encodable {
var brand: String { get set }
}
struct SUVCar : Car {
var brand: String
var weight: Int
}
struct Jeep: Car {
var brand: String
}
struct Sedan: Car {
var brand: String
}