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
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"