web-dev-qa-db-fra.com

Utilisation de JSONEncoder pour coder une variable avec Codable en tant que type

J'ai réussi à faire fonctionner à la fois l'encodage et le décodage JSON et Plist, mais uniquement en appelant directement la fonction encode/décodage sur un objet spécifique.

Par exemple:

struct Test: Codable {
    var someString: String?
}

let testItem = Test()
testItem.someString = "abc"

let result = try JSONEncoder().encode(testItem)

Cela fonctionne bien et sans problèmes.

Cependant, j'essaie d'obtenir une fonction qui accepte uniquement la conformité du protocole Codable en tant que type et enregistre cet objet.

func saveObject(_ object: Encodable, at location: String) {
    // Some code

    let data = try JSONEncoder().encode(object)

    // Some more code
}

Cela entraîne l'erreur suivante:

Impossible d'appeler 'encoder' avec une liste d'arguments de type '(Encodable)'

En regardant la définition de la fonction encode, il semble qu'il devrait pouvoir accepter Encodable, à moins que Value ne soit un type étrange que je ne connaisse pas.

open func encode<Value>(_ value: Value) throws -> Data where Value : Encodable
28
Denis Balko

Utilisez un type générique contraint à Encodable

func saveObject<T : Encodable>(_ object: T, at location: String) {
    //Some code

    let data = try JSONEncoder().encode(object)

    //Some more code
}
60
vadian

J'utiliserais une approche différente de celle consistant à étendre le protocole Encodable avec toutes les méthodes d'instance dont vous pourriez avoir besoin. Vous pouvez également ajouter un paramètre à vos méthodes pour transmettre un encodeur personnalisé et fournir également un encodeur par défaut à toutes les méthodes:

extension Encodable {
    func data(using encoder: JSONEncoder = JSONEncoder()) throws -> Data {
        return try encoder.encode(self)
    }
    func string(using encoder: JSONEncoder = JSONEncoder()) throws -> String {
        return try String(data: encoder.encode(self), encoding: .utf8)!
    }
}

Usage 

let message = ["key":["a","b","c"]]

let jsonData = try! message.data() // 21 bytes [123, 34, 107, 101, 121, 34, 58, 91, 34, 97, 34, 44, 34, 98, 34, 44, 34, 99, 34, 93, 125]
let jsonString = try! message.string()  // "{"key":["a","b","c"]}"

Exemple lors de la transmission d'une date avec un encodeur par défaut. Notez que la dateEncodingStrategy utilisée est la valeur par défaut (un Double représentant la dateIntervalSinceReferenceDate):

let message = ["createdAt": Date()]

let jsonData = try! message.data() // 33 bytes -> [123, 34, 99, 114, 101, 97, 116, 101, 97, 100, 65, 116, 34, 58, 53, 55, 49, 54, 49, 55, 56, 52, 49, 46, 52, 53, 48, 55, 52, 52, 48, 51, 125]
let jsonString = try! message.string()  // {"createdAt":571617841.45074403}"

Vous pouvez maintenant transmettre un encodeur personnalisé à votre méthode pour formater votre Date dans un format lisible par l'homme:

let message = ["createdAt": Date()]
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601
let jsonString = try! message.string(using: encoder)  // "{"createdAt":"2019-02-11T22:48:19Z"}"

Maintenant, en utilisant une structure de message personnalisée

struct Message: Codable {
    let id: Int
    let createdAt: Date
    let sender, title, body: String
}

extension Encodable {
    func sendDataToServer(using encoder: JSONEncoder = JSONEncoder()) throws {
        print(self, terminator: "\n\n")
        // Don't handle the error here. Propagate the error.
        let data = try self.data(using: encoder)
        print(String(data: data, encoding: .utf8)!)
        // following the code to upload the data to the server
        print("Message was successfully sent")
    }
}

let message = Message(id: 1, createdAt: Date(), sender: "[email protected]", title: "Lorem Ipsum", body: """
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
""")

let iso8601 = JSONEncoder()
iso8601.dateEncodingStrategy = .iso8601
iso8601.outputFormatting = .prettyPrinted
do {
    try message.sendDataToServer(using: iso8601)
} catch {
    // handle all errors
    print(error)
}

Cela va imprimer

Message(id: 1, createdAt: 2019-02-11 23:57:31 +0000, sender: "[email protected]", title: "Lorem Ipsum", body: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry\'s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.")

{
  "body" : "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.",
  "id" : 1,
  "sender" : "[email protected]",
  "title" : "Lorem Ipsum",
  "createdAt" : "2019-02-11T23:57:31Z"
}
now just add the code to send the json data to the server
1
Leo Dabus

Vous devez utiliser une fonction générique avec le type générique Encodable

Vous ne pouvez pas

func toData(object: Encodable) throws -> Data {
  let encoder = JSONEncoder()
  return try encoder.encode(object) // Cannot invoke 'encode' with an argument list of type '(Encodable)'
}

Vous pouvez

func toData<T: Encodable>(object: T) throws -> Data {
  let encoder = JSONEncoder()
  return try encoder.encode(object)
}
0
onmyway133