I am attempting to decode a JSON response from a third-party API which contains nested/child JSON that has been base64 encoded.
Contrived Example JSON
{
"id": 1234,
"attributes": "eyAibmFtZSI6ICJzb21lLXZhbHVlIiB9",
}
PS "eyAibmFtZSI6ICJzb21lLXZhbHVlIiB9"
is { 'name': 'some-value' }
base64 encoded.
I have some code that is able to decode this at present but unfortunately I have to reinstanciate an additional JSONDecoder()
inside of the init
in order to do so, and this is not cool...
Contrived Example Code
struct Attributes: Decodable {
let name: String
}
struct Model: Decodable {
let id: Int64
let attributes: Attributes
private enum CodingKeys: String, CodingKey {
case id
case attributes
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(Int64.self, forKey: .id)
let encodedAttributesString = try container.decode(String.self, forKey: .attributes)
guard let attributesData = Data(base64Encoded: encodedAttributesString) else {
fatalError()
}
// HERE IS WHERE I NEED HELP
self.attributes = try JSONDecoder().decode(Attributes.self, from: attributesData)
}
}
Is there anyway to achieve the decoding without instanciating the additional JSONDecoder
?
PS: I have no control over the response format and it cannot be changed.
I find the question interesting, so here is a possible solution which would be to give the main decoder an additional one in its
userInfo
:Because the main method we use from
JSONDecoder()
isfunc decode<T>(_ type: T.Type, from data: Data) throws -> T where T : Decodable
and I wanted to keep it as such, I created a protocol:And I made
JSONDecoder
respects it (and since it already does...)Now, to play a little and check what could be done, I created a custom one, in the idea of having like you said a XML Decoder, it's basic, and it's just for the fun (ie: do no replicate this at home ^^):
So,
init(from:)
:Let's try it now!
Output: