42 votes

Utiliser JSONEncoder pour encoder une variable avec Codable en tant que type

J'ai réussi à obtenir à la fois JSON et des listes de propriétés de codage et de décodage de travail, mais seulement en appelant directement l'encodage/décodage de la fonction 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'essaye de faire une fonction qui prend en seulement l' Codable protocole de conformité 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 se traduit dans le message d'erreur suivant:

Ne peut pas s'appeler "encoder" avec une liste d'arguments de type '(Encodable)'

En regardant la définition de l'encodage de la fonction, il semble comme si elle devrait être en mesure d'accepter Encodable, à moins d' Value est étrange type je ne sais pas de.

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

70voto

vadian Points 29149

Utiliser 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
}
 

2voto

Leo Dabus Points 122890

Je voudrais utiliser une approche différente pour que l'extension de la Encodable protocole avec toutes les méthodes d'instance dont vous pourriez avoir besoin. L'expansion sur que vous pouvez ajouter un paramètre à vos méthodes de passer en paramètre de codeur et de fournir une valeur par défaut encodeur pour tous:

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)!
    }
}

L'utilisation de la

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 du passage d'une date avec une valeur par défaut de l'encodeur. Notez que le dateEncodingStrategy utilisé est la valeur par défaut (un Double représentant la timeIntervalSinceReferenceDate):

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

Maintenant, vous pouvez passer un codeur 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é

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: "user@company.com", 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 permettra d'imprimer

Message(id: 1, createdAt: 2019-02-11 23:57:31 +0000, sender: "user@company.com", 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" : "user@company.com",
  "title" : "Lorem Ipsum",
  "createdAt" : "2019-02-11T23:57:31Z"
}
now just add the code to send the json data to the server

0voto

onmyway133 Points 2196

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)
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X