-
Notifications
You must be signed in to change notification settings - Fork 118
Description
Problem description
Hello friends,
I'd like to decode some XML using DecodableWithConfiguration, instead of the traditional Decodable protocol. (This is because I want to pass some extra data into a type's initializer, and DecodableWithConfiguration can be more typesafe compared with using a userInfo dictionary)
But I can't see any support for DecodableWithConfiguration in XMLCoder.
Example
This example has a parent type Root (which is Decodable) and a collection of Item children (which are DecodableWithConfiguration). The Root initializer decodes its children with decode(_, forKey:, configuration:).
I've included a JSON message, which can be decoded successfully, and an XML message which causes an error to be thrown:
import Foundation
import XMLCoder
let json = """
{
"item": [
{
"myInt": 0,
"myString": "zero"
},
{
"myInt": 1,
"myString": "one"
}
]
}
"""
let xml = """
<root>
<item>
<myInt>0</myInt>
<myString>zero</myString>
</item>
<item>
<myInt>1</myInt>
<myString>one</myString>
</item>
</root>
"""
struct Root: Decodable {
private enum CodingKeys: CodingKey {
case item
}
let item: [Item]
init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self, )
self.item = try container.decode([Item].self, forKey: .item, configuration: "my configuration")
}
}
struct Item: DecodableWithConfiguration {
private enum CodingKeys: CodingKey {
case myInt
case myString
}
let myInt: Int
let myString: String
init(from decoder: any Decoder, configuration: String) throws {
// error thrown here, because `decoder` is trying to return a
// `SingleValueContainer` containing just the `myInt` value
let container = try decoder.container(keyedBy: CodingKeys.self)
self.myInt = try container.decode(Int.self, forKey: .myInt)
self.myString = try container.decode(String.self, forKey: .myString)
}
}
let jsonDecoder = JSONDecoder()
let xmlDecoder = XMLDecoder()
print("JSON:")
let jsonResult = try jsonDecoder.decode(Root.self, from: Data(json.utf8))
print("\(jsonResult)\n")
print("XML:")
let xmlResult = try xmlDecoder.decode(Root.self, from: Data(xml.utf8))
print("\(xmlResult)\n")Running this, I get this output:
JSON:
Root(item: [WithConfiguration.Item(myInt: 0, myString: "zero"), WithConfiguration.Item(myInt: 1, myString: "one")])
XML:
Swift/ErrorType.swift:254: Fatal error: Error raised at top level: Swift.DecodingError.typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "item", intValue: nil), XMLKey(stringValue: "0", intValue: 0)], debugDescription: "Expected to decode Dictionary<String, Any> but found SingleKeyedBox instead.", underlyingError: nil))
The error occurs because the decoder for Item tries to return a SingleValueContainer instead of the expected KeyedValueContainer.
Offer to contribute
I'm happy to work on this, but have some questions first:
- is this a contribution that you'd want to receive?
- am I making a huge complicated job for myself?
- can you offer any guidance about what the task might involve?
Thank-you!
Matthew