如何在使用可编码协议时在输入和输出中使用不同的键?

huangapple go评论105阅读模式
英文:

How to use different keys in input and output when using Encodable protocol?

问题

Expected this: name -> fullname

But the reality that second output produce this:

{
  "name" : "John"
}
英文:

Expected this: name -> fullname

struct Person: Codable {
    let name: String

    enum CodingKeys: String, CodingKey {
        case name = "fullname"
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.name = try container.decode(String.self, forKey: .name)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(name, forKey: .name)
    }
}

let jsonString = """
{
    "name": "John"
}
"""

let jsonDecoder = JSONDecoder()
if let jsonData = jsonString.data(using: .utf8),
   let decodedPerson = try? jsonDecoder.decode(Person.self, from: jsonData) {
    print(decodedPerson.name) // Output: "John"
}

let jsonEncoder = JSONEncoder()
jsonEncoder.outputFormatting = .prettyPrinted
if let encodedData = try? jsonEncoder.encode(decodedPerson),
   let encodedString = String(data: encodedData, encoding: .utf8) {
    print(encodedString)
}

But the reality that second output produce this:

{
  "name" : "John"
}

答案1

得分: 1

If your goal is to decode the name key and output the fullname key, you should update the CodingKeys as follows:

enum CodingKeys: String, CodingKey {
    case fullname = "fullname"
    case name = "name"
}

And modify the encode method to use .fullname instead of .name. Your code should look like this:

struct Person: Codable {
    let name: String

    enum CodingKeys: String, CodingKey {
        case fullname = "fullname"
        case name = "name"
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.name = try container.decode(String.self, forKey: .name)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(name, forKey: .fullname) // Use fullname
    }
}

let jsonString = """
{
    "name": "John"
}
"""

do {
    let jsonData = jsonString.data(using: .utf8)!
    let jsonDecoder = JSONDecoder()
    let decodedPerson = try jsonDecoder.decode(Person.self, from: jsonData)
    print(decodedPerson.name)

    let jsonEncoder = JSONEncoder()
    jsonEncoder.outputFormatting = .prettyPrinted
    let encodedData = try jsonEncoder.encode(decodedPerson)
    let encodedString = String(data: encodedData, encoding: .utf8)!
    print(encodedString)
} catch {
    print(error)
}

The output will be:

John
{
  "fullname" : "John"
}

However, please note that with these changes, your Person class can't decode the JSON it encodes. This might not be what you want.

英文:

If your goal is to decode the name key and output the fullname key (which is really confusing and means your code can't decode the JSON that it generates) then you need to update the CodingKeys to:

enum CodingKeys: String, CodingKey {
    case fullname = "fullname"
    case name = "name"
}

and you need to update the encode method to use .fullname instead of .name.

Then you can parse the original JSON with the name key and then generate new JSON with a fullname key.

Here's your code with some cleanup and the changes I suggest:

struct Person: Codable {
    let name: String

    enum CodingKeys: String, CodingKey {
        case fullname = "fullname"
        case name = "name"
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.name = try container.decode(String.self, forKey: .name)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(name, forKey: .fullname) // Use fullname
    }
}

let jsonString = """
{
    "name": "John"
}
"""

do {
    let jsonData = jsonString.data(using: .utf8)! // this can't fail
    let jsonDecoder = JSONDecoder()
    let decodedPerson = try jsonDecoder.decode(Person.self, from: jsonData)
    print(decodedPerson.name) // Output: "John"

    let jsonEncoder = JSONEncoder()
    jsonEncoder.outputFormatting = .prettyPrinted
    let encodedData = try jsonEncoder.encode(decodedPerson)
    let encodedString = String(data: encodedData, encoding: .utf8)! // This won't fail either
    print(encodedString)
} catch {
    print(error)
}

The output is:

>John
{
"fullname" : "John"
}


Just to reiterate. This is a bad idea. Your Person class, with these changes, can't decode the JSON that it encodes. Are you really sure that is what you want?

huangapple
  • 本文由 发表于 2023年5月22日 12:27:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/76303034.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定