使用JSONEncoder对具有Codable类型的变量进行编码

42

我成功实现了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
}

这会导致以下错误:

无法使用 '(Encodable)' 类型的参数列表调用 'encode'

查看 encode 函数的定义,似乎应该接受 Encodable,除非 Value 是我不知道的某种奇怪类型。

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

9
协议本身不能符合自身,因此您不能将Encodable替换为通用占位符Value,因为Encodable不是符合Encodable的类型。请按照vadian所说使用通用占位符即可。 - Hamish
3个回答

70

使用限制为 Encodable 的通用类型

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

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

    //Some more code
}

当然,谢谢,我还是Swift的新手,完全忘记了这就是它的实现方式。 - Denis Balko
9
什么?有人可以解释一下这个行为吗?我不理解它,毫无意义。 - Erik Mueller
3
Codable需要确定其对象类型。使用Any作为类型会使它感到困惑,因为它不知道应调用哪个类型的init(from decoder:)初始化器。此函数基本上以泛型的形式提供了缺失的信息。代码可以通过类型推断计算要使用哪种类型。 - Ash
它需要知道(或推断)您要编码的类型,因为如果您有 var x: Encodable = myXStruct();var y: Encodable = myYStruct();。编码函数将不知道那是什么。是x还是y。因此,您应该查找具有关联类型(PAT)的通用函数和协议。 - Siempay
类似于 var object<T : Encodable>: T { 这样的代码是无法编译通过的。 - pkamb
显示剩余2条评论

2
我会采用不同的方法来扩展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是默认值(代表自2001年1月1日午夜以来经过的时间的Double类型):

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

这将打印
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

0

你需要使用泛型函数和泛型类型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)
}

我对此寄予厚望,但是仍然出现了同样的错误:let bufferReq: Encodable func toData(object: T) throws -> Data { let encoder = JSONEncoder() return try encoder.encode(object) } do { let foo = toData(object: bufferReq) // 仍然有相同的错误 } - David H

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接