Encoding to dictionary

I'm completely lost in encoding classes to Dictionary


I have for instance the following class


class AdjectifsModel: BaseModel {
    var id: Int = 0
    var idmot: Int = 0
    var idtypemot: Int = 0
    var idracine: Int = 0
    var racine: String = ""
    var francais: String = ""
    var msingulier: String = ""
    var fsingulier: String = ""
    var mpluriel: String = ""
    var fpluriel: String = ""
    var commentaire: String = ""
    var erreur: Int = 0
    var nbliens: Int = 0

    enum CodingKeys: String, CodingKey {
        case id
        case idmot
        case idtypemot
        case idracine
        case racine
        case francais
        case msingulier
        case fsingulier
        case mpluriel
        case fpluriel
        case commentaire
        case erreur
        case nbliens
    }
}


No problem to encode it with the following code:


required convenience init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.init()
        self.id          = try container.decoder(Int.self, forKey: .id)
        self.idmot       = try container.decoder(Int.self, forKey: .idmot)
        self.idtypemot   = try container.decoder(Int.self, forKey: .idtypemot)
        self.idracine    = try container.decoder(Int.self, forKey: .idracine)
        self.racine      = try container.decoder(String.self, forKey: .racine)
        self.francais    = try container.decoder(String.self, forKey: .francais)
        self.msingulier  = try container.decoder(String.self, forKey: .msingulier)
        self.fsingulier  = try container.decoder(String.self, forKey: .fsingulier)
        self.mpluriel    = try container.decoder(String.self, forKey: .mpluriel)
        self.fpluriel    = try container.decoder(String.self, forKey: .fpluriel)
        self.commentaire = try container.decoder(String.self, forKey: .commentaire)
        self.erreur      = try container.decoder(Int.self, forKey: .erreur, defaut: 0)
        self.nbliens     = try container.decoder(Int.self, forKey: .nbliens, defaut: 0)
    }


But in order to save my class, I need to transform it in a Dictionary [String: String] and I don't kown how to do it


I found on the internet the following code:


class DictionaryEncoder {
    private let encoder = JSONEncoder()

    var dateEncodingStrategy: JSONEncoder.DateEncodingStrategy {
        set { encoder.dateEncodingStrategy = newValue }
        get { return encoder.dateEncodingStrategy }
    }

    var dataEncodingStrategy: JSONEncoder.DataEncodingStrategy {
        set { encoder.dataEncodingStrategy = newValue }
        get { return encoder.dataEncodingStrategy }
    }

    var nonConformingFloatEncodingStrategy: JSONEncoder.NonConformingFloatEncodingStrategy {
        set { encoder.nonConformingFloatEncodingStrategy = newValue }
        get { return encoder.nonConformingFloatEncodingStrategy }
    }

    var keyEncodingStrategy: JSONEncoder.KeyEncodingStrategy {
        set { encoder.keyEncodingStrategy = newValue }
        get { return encoder.keyEncodingStrategy }
    }

    func encode(_ value: T) throws -> [String: Any] where T : Encodable {
        let data = try encoder.encode(value)
        return try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String: Any]
    }
}


I copy it (and I must admit that I don't undestand how it works).

If my class contains only strings property, the following Code works:


var params = try DictionaryEncoder().encode(self)


but if the properties are mixed (Strings and Int) it doesn't work

Answered by DTS Engineer in 412423022

This is working for me. Here’s a cut down version of your code:

import Foundation

class AdjectifsModel: Codable {
    var id: Int = 0
    var racine: String = ""

    enum CodingKeys: String, CodingKey {
        case id
        case racine
    }
}

class DictionaryEncoder {
    private let encoder = JSONEncoder()

    func encode<T>(_ value: T) throws -> [String: Any] where T : Encodable {
        let data = try encoder.encode(value)
        return try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String: Any]
    }
}

let obj = AdjectifsModel()
let params = try DictionaryEncoder().encode(obj)
print(params)
// ["racine": , "id": 0]

Most of the stuff I elided for simplicity, but there’s one potentially significant change that I had to make because your post is missing some details, namely the declaration of

BaseObject
. Implementing
Codable
in a class hierarchy is a little tricky. Here’s an example of how you might do that:
class BaseObject: Codable {
    var uuid: UUID
    init() {
        self.uuid = UUID()
    }
}

class AdjectifsModel: BaseObject {
    var id: Int
    var racine: String

    override init() {
        self.id = 0
        self.racine = ""
        super.init()
    }

    required init(from decoder: Decoder) throws {
        fatalError()
    }

    override func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try super.encode(to: container.superEncoder())
        try container.encode(self.id, forKey: .id)
        try container.encode(self.racine, forKey: .racine)
    }

    enum CodingKeys: String, CodingKey {
        case id
        case racine
    }
}

class DictionaryEncoder {
    private let encoder = JSONEncoder()

    func encode<T>(_ value: T) throws -> [String: Any] where T : Encodable {
        let data = try encoder.encode(value)
        return try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String: Any]
    }
}

let obj = AdjectifsModel()
let params = try DictionaryEncoder().encode(obj)
print(params)
// ["super": {
//     uuid = "E328BB0B-B892-458B-B7E7-ED7C00A3F2D6";
// }, "id": 0, "racine": ]

Note the nested dictionary here. The encoding architecture uses this to prevent key clashes between the properties in the class and the properties in the superclass.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
Accepted Answer

This is working for me. Here’s a cut down version of your code:

import Foundation

class AdjectifsModel: Codable {
    var id: Int = 0
    var racine: String = ""

    enum CodingKeys: String, CodingKey {
        case id
        case racine
    }
}

class DictionaryEncoder {
    private let encoder = JSONEncoder()

    func encode<T>(_ value: T) throws -> [String: Any] where T : Encodable {
        let data = try encoder.encode(value)
        return try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String: Any]
    }
}

let obj = AdjectifsModel()
let params = try DictionaryEncoder().encode(obj)
print(params)
// ["racine": , "id": 0]

Most of the stuff I elided for simplicity, but there’s one potentially significant change that I had to make because your post is missing some details, namely the declaration of

BaseObject
. Implementing
Codable
in a class hierarchy is a little tricky. Here’s an example of how you might do that:
class BaseObject: Codable {
    var uuid: UUID
    init() {
        self.uuid = UUID()
    }
}

class AdjectifsModel: BaseObject {
    var id: Int
    var racine: String

    override init() {
        self.id = 0
        self.racine = ""
        super.init()
    }

    required init(from decoder: Decoder) throws {
        fatalError()
    }

    override func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try super.encode(to: container.superEncoder())
        try container.encode(self.id, forKey: .id)
        try container.encode(self.racine, forKey: .racine)
    }

    enum CodingKeys: String, CodingKey {
        case id
        case racine
    }
}

class DictionaryEncoder {
    private let encoder = JSONEncoder()

    func encode<T>(_ value: T) throws -> [String: Any] where T : Encodable {
        let data = try encoder.encode(value)
        return try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String: Any]
    }
}

let obj = AdjectifsModel()
let params = try DictionaryEncoder().encode(obj)
print(params)
// ["super": {
//     uuid = "E328BB0B-B892-458B-B7E7-ED7C00A3F2D6";
// }, "id": 0, "racine": ]

Note the nested dictionary here. The encoding architecture uses this to prevent key clashes between the properties in the class and the properties in the superclass.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thank you Claude


In fact what was missing was the encode function, as my Class is derived not from Codable but from another class

You should thank Quinn, I did nothing but learn from his answer 😁

Ho yes, sorry my mistake


So thank you Quinn

Encoding to dictionary
 
 
Q