How to extend float3 or any other built-in type to conform to the Codable protocol?

前端 未结 3 923
感动是毒
感动是毒 2021-01-18 15:14

In trying to serialize an array of float3 objects with the basic JSONEncoder, it\'s revealed that float3 does not conform to the Codable protocol, so this cannot be done.

3条回答
  •  孤独总比滥情好
    2021-01-18 15:50

    You can solve the compiler error by instead of trying to directly assign the decoded values to the fields of your type, storing the decoded values in local variables, then calling a designated initializer of float3.

    As Rob mentions in his answer, the cause of the issue has to do with x, y and z being computed properties rather than stored ones, so they cannot be directly written during initialization.

    extension float3: Codable {
        public init(from decoder: Decoder) throws {
            let values = try decoder.container(keyedBy: CodingKeys.self)
            let x = try values.decode(Float.self, forKey: .x)
            let y = try values.decode(Float.self, forKey: .y)
            let z = try values.decode(Float.self, forKey: .z)
            self.init(x, y, z)
        }
    
        public func encode(to encoder: Encoder) throws {
            var container = encoder.container(keyedBy: CodingKeys.self)
            try container.encode(x, forKey: .x)
            try container.encode(y, forKey: .y)
            try container.encode(z, forKey: .z)
        }
    
        private enum CodingKeys: String, CodingKey {
            case x,y,z
        }
    }
    

    You can test both encoding and decoding in a Playground using below code:

    let vector = float3(3, 2.4, 1)
    do {
        let encodedVector = try JSONEncoder().encode(vector)
        let jsonVector = String(data: encodedVector, encoding: .utf8) //"{"x":3,"y":2.4000000953674316,"z":1}"
        let decodedVector = try JSONDecoder().decode(float3.self, from: encodedVector) //float3(3.0, 2.4, 1.0)
    } catch {
        print(error)
    }
    

    If you prefer more concise code, the init(from decoder:) method can be shortened to:

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

提交回复
热议问题