Использование JSONEncoder для кодирования переменной с типом Codable as

Мне удалось заставить работать кодирование и декодирование JSON и plist, но только путем прямого вызова функции кодирования / декодирования для определенного объекта.

Например:

struct Test: Codable {
    var someString: String?
}

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

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

Это работает хорошо и без проблем.

Однако я пытаюсь получить функцию, которая принимает только соответствие протоколу Codable как тип и сохраняет этот объект.

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

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

    // Some more code
}

Это приводит к следующей ошибке:

Невозможно вызвать 'encode' со списком аргументов типа '(Encodable)'

Глядя на определение функции кодирования, кажется, что она должна принимать Encodable, если только Value не является каким-то странным типом, о котором я не знаю.

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

person Denis Balko    schedule 12.07.2017    source источник
comment
Протоколы не соответствуют сами себе, поэтому вы не можете заменить общий заполнитель Value на Encodable, поскольку Encodable не является типом что соответствует Encodable. Как говорит Вадиан, просто используйте общий заполнитель.   -  person Hamish    schedule 12.07.2017


Ответы (3)


Используйте общий тип с ограничением Encodable

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

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

    //Some more code
}
person vadian    schedule 12.07.2017
comment
Конечно, спасибо, я все еще новичок в Swift и совершенно забыл, как это делается. - person Denis Balko; 12.07.2017
comment
что за? может кто-нибудь объяснить мне такое поведение? для меня это не имеет никакого смысла - person Erik Mueller; 15.02.2018
comment
Codable должен иметь возможность определять тип своего объекта. Использование Any в качестве типа сбивает его с толку, потому что он не знает, init(from decoder:) инициализатор какого типа вызывать. Эта функция по существу предоставляет недостающую информацию в виде универсального. Код может вычислить, какой тип использовать, посредством вывода типа. - person Ash; 01.05.2018
comment
ему нужно знать (или сделать вывод) тип, который вы хотите кодировать, потому что, если у вас есть var x: Encodable = myXStruct(); и var y: Encodable = myYStruct(); . функция кодирования не знает, что это такое. это х или у. поэтому вам следует искать общие функции и протоколы со связанными типами (PAT) - person brahimm; 04.10.2018
comment
Каков синтаксис для этого в получателе свойства, а не в функции? - person pkamb; 30.10.2018
comment
@pkamb Извините, я не понимаю, о чем вы. - person vadian; 30.10.2018
comment
Что-то вроде var object<T : Encodable>: T {, которое не компилируется. - person pkamb; 30.10.2018
comment
@pkamb вы не можете объявить вычисляемое свойство как универсальное и / или заставить его выдавать ошибку, но вы можете объявить его как свойство экземпляра, расширяющее протокол Encodable (вам нужно будет обработать или проигнорировать вашу ошибку там). - person Leo Dabus; 12.02.2019

Я бы использовал другой подход к расширению протокола Encodable со всеми методами экземпляра, которые могут вам понадобиться. Расширяя это, вы можете добавить параметр к своим методам, чтобы передать пользовательский кодировщик и предоставить кодировщик по умолчанию для всех из них:

extension DataProtocol {
    var string: String? { String(bytes: self, encoding: .utf8) }
}

extension Encodable {
    func data(using encoder: JSONEncoder = JSONEncoder()) throws -> Data { try encoder.encode(self) }
    func string(using encoder: JSONEncoder = JSONEncoder()) throws -> String { try data(using: encoder).string ?? "" }
}

использование

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

Пример передачи даты с кодировщиком по умолчанию. Обратите внимание, что по умолчанию используется dateEncodingStrategy (Double, представляющий 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}"

Теперь вы можете передать собственный кодировщик в свой метод, чтобы отформатировать дату в удобочитаемом формате:

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

Теперь используется настраиваемая структура сообщений

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(data.string!)
        // 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)
}

Это напечатает

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
person Leo Dabus    schedule 12.02.2019

Вам нужно использовать универсальную функцию с универсальным типом Encodable

Ты не можешь

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

Вы можете

func toData<T: Encodable>(object: T) throws -> Data {
  let encoder = JSONEncoder()
  return try encoder.encode(object)
}
person onmyway133    schedule 12.06.2018
comment
Я возлагал большие надежды на эту, но ту же ошибку: `let bufferReq: Encodable func toData‹ T: Encodable ›(object: T) throws -› Data {let encoder = JSONEncoder () return try encoder.encode (object)} do {let foo = toData (object: bufferReq) // все еще та же ошибка ` - person David H; 20.08.2018