Skip to content

Decoding 3rd party Codable data structures #243

@kamcma

Description

@kamcma

Was having trouble decoding into IdentifiedArray, a data structure from @pointfreeco. It conforms to Codable. Came up with the following minimal reproduction:

import IdentifiedCollections
import XCTest
import XMLCoder

let sourceXML: String = """
<?xml version="1.0" encoding="utf-8"?>
<team>
  <user>
    <id>1</id>
    <name>Bob</name>
  </user>
  <user>
    <id>2</id>
    <name>Alice</name>
  </user>
</team>
"""

struct User: Decodable, Identifiable {
  let id: Int
  let name: String
}

struct Team: Decodable {
  let users: [User]

  enum CodingKeys: String, CodingKey {
    case users = "user"
  }
}

struct IdentifiableTeam: Decodable {
  let users: IdentifiedArrayOf<User>

  enum CodingKeys: String, CodingKey {
    case users = "user"
  }
}

final class XMLCoderTests: XCTestCase {
  func testDecodeArray() throws {
    let decoder = XMLDecoder()
    guard let data = sourceXML.data(using: .utf8) else { XCTFail(); return }
    let team = try decoder.decode(Team.self, from: data)
    XCTAssertEqual(2, team.users.count)
  }

  func testDecodeIdentifiedArray() throws {
    let decoder = XMLDecoder()
    guard let data = sourceXML.data(using: .utf8) else { XCTFail(); return }
    let team = try decoder.decode(IdentifiableTeam.self, from: data) // ❌ throws
    XCTAssertEqual(2, team.users.count)
  }
}

I would expect both tests to pass. The vanilla array decoding does pass, but the IdentifiedArray decoding throws the following:

testDecodeIdentifiedArray(): failed: caught error: "typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "user", intValue: nil), XMLKey(stringValue: "0", intValue: 0), XMLKey(stringValue: "0", intValue: 0)], debugDescription: "Expected to decode Dictionary<String, Any> but found ChoiceBox instead.", underlyingError: nil))"

Had a little trouble understanding XMLCoder's internals, but did notice that it extends a few standard library data structures to conform to an internal protocol AnySequence. If I change my minimal reproduction like so:

-import XMLCoder
+@testable import XMLCoder

// ...

+extension IdentifiedArray: AnySequence where Element: Identifiable, ID == Element.ID { }

then both tests pass.

Are my findings correct that conformance to this internal protocol are necessary to decode arbitrary data structures from outside the standard library?

I think ideally this would not be the case, and the encoder/decoder implementations would support arbitrary Codable types generally. But I understand the maintainers of the library are not the original authors.

In the mean time, if conformance to AnySequence is required, what do the maintainers think about making it public?

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