1

I have such an object that should be encoded to JSON (My Playground example)

struct Toy: Codable {
    var name: String
    
    enum GiftKeys: String, CodingKey {
        case toy = "name"
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: GiftKeys.self)
        try container.encode(self, forKey: .toy)
    }
}

struct Employee: Encodable {
    var name: String
    var id: Int
    var favoriteToy: Toy
    
    enum CodingKeys: CodingKey {
        case name, id
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(name, forKey: .name)
        try container.encode(id, forKey: .id)
        try favoriteToy.encode(to: encoder)
    }
}

do {
    let toy = Toy(name: "Teddy Bear")
    let employee = Employee(name: "John Appleseed", id: 7, favoriteToy: toy)
    
    let encoder = JSONEncoder()
    let nestedData: Data = try encoder.encode(employee)
    let nestedString = String(data: nestedData, encoding: .utf8)!
    print(nestedString)
}

What I am going to achieve is that each object knows how to encode itself. So, in my example when I through the employee object to the encoder, so each of the objects (employee and Toy) is responsible for its encoding.

But when I try to run the example in the Playground looks like it comes to the stuck in the line try favoriteToy.encode(to: encoder)

What is the problem here?

2
  • I think you have a misunderstanding of how the encodable API works. Is this your first time encoding nested objects? Commented Dec 23, 2021 at 11:54
  • @Sweeper yes, but I try follow the tutorials Commented Dec 23, 2021 at 13:13

2 Answers 2

2

This line is the problem:

try container.encode(self, forKey: .toy)

The toy is trying to encode itself, so this will call encode(to:) again, causing infinite recursion. You probably meant:

try container.encode(name, forKey: .toy)

Remember that in the encode(to:) method, you are trying to answer the question of "how to encode a Toy?". If you answer with "just encode the toy itself!" That's not really answering the question, is it?

This will print:

{"name":"Teddy Bear","id":7}

Notice that the employee's name is overwritten by the toy's name, since you encoded the toy's name using the same encoder and same key, as the employee's name.

That's probably not what you intended. You probably wanted:

enum CodingKeys: CodingKey {
    case name, id, toy
}

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

// {"name":"John Appleseed","id":7,"toy":{"name":"Teddy Bear"}}

Note that all this code can be generated by the compiler, if you remove all your CodingKeys and encode methods:

struct Toy: Codable {
    var name: String
}
struct Employee: Codable {
    var name: String
    var id: Int
    var favoriteToy: Toy
}
Sign up to request clarification or add additional context in comments.

Comments

1

What I am going to achieve is that each object knows how to encode itself.

Each object does know to encode itself if you leave it alone by omitting the CodingKeys and encode(to methods

struct Toy: Encodable {
    var name: String
}

struct Employee: Encodable {
    var name: String
    var id: Int
    var favoriteToy: Toy
}

You made two serious mistakes. If you implement encode(to you have to encode each struct member for the corresponding CodingKey rather than encoding self or calling encode(to: on a struct member.

So replace

try container.encode(self, forKey: .toy)

with

try container.encode(name, forKey: .toy) 

and replace

try favoriteToy.encode(to: encoder)

with

try container.encode(favoriteToy, forKey: .favoriteToy)

and add also the missing CodingKey favoriteToy and the raw value type

private enum CodingKeys: String, CodingKey {
    case name, id, favoriteToy
}

But in this case the best solution is not to implement encode(to

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.