In Swift, use a global CodingKeys (enum or struct) keeping my objects as simple as possible

120 Views Asked by At

In a simplified example, I have these JSONs Strings

let jsonStringBird = """
{
    "identifier": "0001ABX",
    "name": "Doroty",
    "species_name": "Gorrion",
}
"""


let jsonStringPlane = """
{
    "identifier": "PL1803",
    "name": "Boing 777",
    "number_sits_in_plane": 8,
}
"""

And I have a lot of structures like these ones (they are just examples)

struct Bird: Decodable {
    var identifier: String
    var name: String
    var species: String
}

struct Plane: Decodable {
    var identifier: String
    var name: String
    var sits: String
}

So, I want to decode my structs without modify them (avoid overriding the init from decoder method and avoid having a private CodingKeys enum).

Maybe having a global CodingKey enum/struct like this one, but I don't know how to tell to the JSONDecoder to use the following CodingKey for all my structs (I only know how to do it in the init from decoder method)

enum MyCodingKeys: String, CodingKey {
    case identifier
    case name
    case species = "species_name"
    case sits = "number_sits_in_plane"
}

My point is that I have a lot of those structures, and I would prefer to keep them as simple as posible

Notes

In my real project I'm actually using CBORCoding where I'm mapping [Int, AnyCodable] properties, not [String, AnyCodable].

So, for the question here is important to use JSONDecoder and use CodingKeys. That way I could translate it to CBORCoding

1

There are 1 best solutions below

1
soundflix On

Here's an approach with JSONSerialization that works with your example.

+ it does not need any CodingKey.

+ it is totally flexible with what and how many attributes your object will have.

- all attributes are stored in a dictionary with the values of type Any.

- only one object type.

struct myObject {
    let identifier: String
    let name: String
    let attributes: [String: Any]
}

func myObjectFromJSON(_ jsonString: String) -> myObject? {
    let data = Data(jsonString.utf8)
    
    if var json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
        print(type(of: json), json) // Dictionary<String, Any> ["species_name": Gorrion, "identifier": 0001ABX, "name": Doroty]
        
        if let identifier = json["identifier"] as? String, let name = json["name"] as? String {
            json.removeValue(forKey: "identifier")
            json.removeValue(forKey: "name")
            let newObject = myObject(identifier: identifier, name: name, attributes: json)
            return newObject
        }
    }
    return nil
}

Usage:

let object1 = myObjectFromJSON(jsonStringBird)
let object2 = myObjectFromJSON(jsonStringPlane)