Custom Dictionary Encoder & Decoder for the Codable protocol in Swift 4
Asked Answered
O

1

7

If I have a struct that conforms to the Codable protocol like so:

enum AnimalType: String, Codable {
    case dog
    case cat
    case bird
    case hamster
}

struct Pet: Codable {
    var name: String
    var animalType: AnimalType
    var age: Int
    var ownerName: String
    var pastOwnerName: String?
}

How can I create an encoder & a decoder that encodes / decodes it to / from an instance of type Dictionary<String, Any?> like so?

let petDictionary: [String : Any?] = [
    "name": "Fido",
    "animalType": "dog",
    "age": 5,
    "ownerName": "Bob",
    "pastOwnerName": nil
]
let decoder = DictionaryDecoder()
let pet = try! decoder.decode(Pet.self, for: petDictionary)

NB: I'm aware that it's possible to use the JSONEncoder and JSONDecoder classes before casting the result to a dictionary object, but I don't want that for efficiency reasons.

The Swift standard library comes with the JSONEncoder and JSONDecoder as well as the PListEncoder and PListDecoder classes right out the box which conform to the Encoder and Decoder protocols respectively.

My problem is that I have no clue how to implement these protocols for my custom encoder and decoder classes:

class DictionaryEncoder: Encoder {

    var codingPath: [CodingKey]

    var userInfo: [CodingUserInfoKey : Any]

    func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key : CodingKey {

    }

    func unkeyedContainer() -> UnkeyedEncodingContainer {

    }

    func singleValueContainer() -> SingleValueEncodingContainer {

    }
}
class DictionaryDecoder: Decoder {

    var codingPath: [CodingKey]

    var userInfo: [CodingUserInfoKey : Any]

    func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> where Key : CodingKey {

    }

    func unkeyedContainer() throws -> UnkeyedDecodingContainer {

    }

    func singleValueContainer() throws -> SingleValueDecodingContainer {

    }
}

Given that Swift is open sourced, it's possible to view the source code of the JSONEncoder and PListEncoder classes in the standard library, but the source files are huge and difficult to understand due to the lack of documentation apart from a few comments.

Ostraw answered 19/10, 2017 at 13:9 Comment(3)
If you don't want to use JSONDecoder then why implement the Codable protocols?Entozoon
The Codable protocol is generalized / generic and can be used to represent external data structures with native Swift types. The Swift standard library has the Encoder and Decoder protocols which you can implement to create your own custom encoder & decoder for the Codable protocol. The Swift standard library comes with two such Encoder / Decoder pairs: github.com/apple/swift/blob/master/stdlib/public/SDK/Foundation/… github.com/apple/swift/blob/master/stdlib/public/SDK/Foundation/…Eleaseeleatic
My problem is that the code there is far too complex and apart from the comments in the code base there's no documentation that explains how you can implement your own custom encoder & decoder that conform to the Encoder and Decoder protocols respectively.Eleaseeleatic
F
1

If you look at the implementation of JSONDecoder (here), you'll see that it's a two-step process: 1. use JSONSerialization to convert the Data into an json dictionary, then 2. create an instance of an internal class _JSONDecoder to do the dictionary -> Codable object conversion.

There is some discussion on the Swift forums on possibly exposing the internal types, and the Swift team may do something about it in the future. Someone also offered up a 3rd party framework for doing what you want.

Fidole answered 24/1, 2018 at 16:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.