# How to encode objects of unknown type?

**URL:** <https://forums.swift.org/t/how-to-encode-objects-of-unknown-type/12253>\
**Category:** Using Swift\
**Created:** [April 29, 2018, 11:40pm UTC](https://forums.swift.org/t/how-to-encode-objects-of-unknown-type/12253 "2018-04-29T23:40:47Z")\
**Posts on this page:** 1\
**Showing post:** 11

<div class="post-metadata">

**Author:** ![itaiferber](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/itaiferber/32/33840_2.png) [@itaiferber](https://forums.swift.org/u/itaiferber)\
**Post date:** [May 23, 2018, 4:40pm UTC](https://forums.swift.org/t/how-to-encode-objects-of-unknown-type/12253/11 "2018-05-23T16:40:26Z")

</div>

Sure, happy to elaborate!

> [@gwendal.roue](#):
>
> So you say that this code is correct (although it does not compile):
> 
> ```swift
> func encode(to encoder: Encoder) throws {
> var container = encoder.singleValueContainer()
> try value.encode(to: &container) // isn't it container.encode(value) here, instead?
> }
> 
> ```

Did you include the necessary extension on `Encodable`?

```swift
extension Encodable {
    fileprivate func encode(to container: inout SingleValueEncodingContainer) throws {
        try container.encode(self)
    }
}

```

If you did and this still isn't compiling for you, can you show your full code? (And what version of Swift are you using?)

> [@gwendal.roue](#):
>
> And that this code is incorrect:
> 
> ```swift
> func encode(to encoder: Encoder) throws {
> try self.value.encode(to: encoder)
> }
> 
> ```

To elaborate on this point: when you execute `value.encode(to: encoder)`, you are directly invoking `value`'s `encode(to:)` method, which directly allows it to request containers and encode in its preferred representation. For the vast majority of types out there, this behaves as expected.

This breaks down, though, for any type `T` whose preferred representation differs from the `Encoder`'s preferred representation for the type. Let's use `URL` as an example, whose [`encode(to:)` reads as follows:](https://github.com/apple/swift/blob/master/stdlib/public/SDK/Foundation/URL.swift#L1230)

```swift
public func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(self.relativeString, forKey: .relative)
    if let base = self.baseURL {
        try container.encode(base, forKey: .base)
    }
}

```

`URL` itself prefers a keyed container which allows it to encode its base and relative string separately, to preserve them. This representation makes sense for `URL` itself, but it would be pretty surprising to encode a `URL` with `JSONEncoder` and get out a dictionary (e.g. `{"relative": "http://swift.org"}`) rather than a string representation (`"http://swift.org"`). `JSONEncoder`, then, [intercepts the `URL` type](https://github.com/apple/swift/blob/master/stdlib/public/SDK/Foundation/JSONEncoder.swift#L832) when it is encoded to encode the preferred string representation:

```swift
fileprivate func box_<T : Encodable>(_ value: T) throws -> NSObject? {
    // ...
    if T.self == URL.self || T.self == NSURL.self {
        // Encode URLs as single strings.
        return self.box((value as! URL).absoluteString)
    }

    // ...
}

```

This works, of course, only when the encoder gets a chance to see that what's being encoded is a `URL`.

So the key difference is that `myURL.encode(to: encoder)` asks `URL` to encode directly into the encoder by requesting a keyed container, while `encoder.singleValueContainer().encode(myURL)` gives the encoder a chance (via the single-value container) to see that what's actually being encoded is a `URL` instead of just seeing its contents.

The code above offers the latter option via `try container.encode(self)` rather than the simpler `self.encode(to: container)`. You can see the effect of this in the following:

```swift
import Foundation

extension Encodable {
    fileprivate func encode(to container: inout SingleValueEncodingContainer) throws {
        try container.encode(self)
    }
}

struct AnyEncodable1 : Encodable {
    var value: Encodable
    init(_ value: Encodable) {
        self.value = value
    }
    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try value.encode(to: &container)
    }
}

struct AnyEncodable2 : Encodable {
    var value: Encodable
    init(_ value: Encodable) {
        self.value = value
    }
    func encode(to encoder: Encoder) throws {
        try value.encode(to: encoder)
    }
}

struct MyThing : Encodable {
    let myURL = AnyEncodable1(URL(string: "http://swift.org")!)
}

let url = URL(string: "http://swift.org")
let encoder = JSONEncoder()
var data = try encoder.encode(MyThing())
print(String(data: data, encoding: .utf8)!) // => {"myURL":"http:\/\/swift.org"}

```

Changing the above to

```swift
struct MyThing : Encodable {
    let myURL = AnyEncodable2(URL(string: "http://swift.org")!)
}

```

produces `{"myURL":{"relative":"http:\/\/swift.org"}}`.

> [@gwendal.roue](#):
>
> But.... how is anyone, generally, supposed to know which container to create? `singleValueContainer()`? `unkeyedContainer()`? `container(keyedBy:)`? And if the correct choice is `container(keyedBy:)`, what is the CodingKey type?
> 
> See, for an example of "incorrect" use: [https://github.com/groue/GRDB.swift/blob/1a91020a8406b031141a17883a47293a2ca62528/GRDB/Record/PersistableRecord%2BEncodable.swift#L205-L206](https://github.com/groue/GRDB.swift/blob/1a91020a8406b031141a17883a47293a2ca62528/GRDB/Record/PersistableRecord%2BEncodable.swift#L205-L206) I know that `container(keyedBy:)` is needed here, but I don't know the correct CodingKey type.
> 
> Thoughts?

The container should always be a `singleValueContainer()`. Encoding a value into a single-value container is equivalent to encoding the value directly into the encoder, with the primary difference being the above: encoding into the encoder writes the _contents_ of a type into the encoder, while encoding to a single-value container gives the encoder a chance to intercept the type as a whole.

(I can elaborate on this point if it would help.)

---

_[View the full topic](https://forums.swift.org/t/how-to-encode-objects-of-unknown-type/12253)._
