Переопределить реализацию Codable в структуре - PullRequest
0 голосов
/ 09 мая 2020

Есть ли способ переопределить реализацию Codable для структуры?

Код, который нельзя изменить:

struct obj1 : Codable {
   var values:[Int]
   ...
}

Желаемый JSON:

{ "x": 1, "y": 2, "z": 3 }

Это не работает:

extension obj1 {
   init(from decoder: Decoder) throws {
      //This is never called
   }

   func encode(to encoder: Encoder) throws {
      //This is never called
   }
}

Предыстория:

Я сейчас портирую существующее приложение на iOS и конвертирую его в Swift из смеси C ++ и Objective- C. Это приложение использует Metal для создания 3D-графики и поэтому широко использует математические конструкции, такие как Vector, Matrix, Quaternion и т. Д. c.

В начале проекта я решил использовать для них библиотеку simd от Apple. различные типы. Я дал им псевдонимы, соответствующие объектам, которые у нас уже есть, чтобы облегчить перенос, и расширения для добавления различных функций, которые не были встроены. Все это отлично работает.

Теперь я нахожусь в той точке, где мне нужно чтобы иметь возможность преобразовать эти типы в / из JSON, и я столкнулся с проблемой. Структуры simd уже поддерживают Codable, но они используют формат, отличный от того, который мне нужно поддерживать. Поскольку simd использует структуры, я не могу получить от него собственное определение init(from) и encode. Также кажется, что я тоже не могу поместить свое собственное определение в расширение (компилятор не жалуется, но мои версии тоже не вызываются).

В настоящее время у меня это работает с использованием структуры оболочка (ie, Vector3Codable). Однако это означает, что любой из моих объектов, которые имеют эти члены simd, также должен предоставлять пользовательские реализации Codable, вместо этого используя поддержку по умолчанию, которую добавляет компилятор.

Я бы предпочел придерживаться simd, если это возможно, поскольку в отличие от предоставления моей собственной реализации для всего.

Изменить. Похоже, что мой упрощенный пример кода действительно работает (ох!). Вот фактический код, который не работает. Судя по всему, проблема сложнее, чем я думал изначально.

typealias Vector3 = SIMD3<Float>

extension Vector3 {
    enum CodingKeys: String, CodingKey {
        case x
        case y
        case z
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let x = try container.decodeIfPresent(Float.self, forKey: .x) ?? 0.0
        let y = try container.decodeIfPresent(Float.self, forKey: .y) ?? 0.0
        let z = try container.decodeIfPresent(Float.self, forKey: .z) ?? 0.0

        self.init(x, y, z)
    }
}

1 Ответ

2 голосов
/ 09 мая 2020

РЕДАКТИРОВАТЬ после обновления вопроса

Это правда, что вы не можете переопределить кодируемое поведение SIMD3 в расширении. Однако вы можете попытаться обернуть свои SIMD-файлы в тривиальные структуры пересылки, которые затем позволят вам управлять их кодировкой:

struct Vector3 { // instead of the typealias
    private var simd: SIMD3<Float>
    var x: Float { return simd.x }
    var y: Float { return simd.y }
    var z: Float { return simd.z }
    // add forwarders for other SIMD methods/properties you use

    init(_ x: Float, _ y: Float, _ z: Float) {
        simd = SIMD3(x, y, z)
    }
}

extension Vector3: Codable {
    enum CodingKeys: String, CodingKey {
        case x, y, z
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let x = try container.decodeIfPresent(Float.self, forKey: .x) ?? 0.0
        let y = try container.decodeIfPresent(Float.self, forKey: .y) ?? 0.0
        let z = try container.decodeIfPresent(Float.self, forKey: .z) ?? 0.0

        self.init(x, y, z)
    }
}

(для краткости я включил только геттеры и Decodable)


Это работает должным образом:

struct Obj1 : Codable {
   var values:[Int]
}

extension Obj1 {
    enum CodingKeys: String, CodingKey {
        case x, y, z
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let x = try container.decode(Int.self, forKey: .x)
        let y = try container.decode(Int.self, forKey: .y)
        let z = try container.decode(Int.self, forKey: .z)

        self.values = [x, y, z]
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(values[0], forKey: .x)
        try container.encode(values[1], forKey: .y)
        try container.encode(values[2], forKey: .z)
    }
}

let obj = Obj1(values: [7, 42, 17])

do {
    let data = try JSONEncoder().encode(obj)
    print(String(bytes: data, encoding: .utf8)!) // "{"x":7,"y":42,"z":17}"

    let o = try JSONDecoder().decode(Obj1.self, from: data)
    print(o.values) // [7, 42, 17]
} catch {
    print(error)
}

Когда ваши реализации никогда не вызываются, покажите код, который кодирует / декодирует ваши obj1 экземпляры.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...