Skip to content

No support for DecodableWithConfiguration #294

@mflint

Description

@mflint

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions