Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decoding 3rd party Codable data structures #243

Closed
kamcma opened this issue Jul 28, 2022 · 3 comments · Fixed by #244
Closed

Decoding 3rd party Codable data structures #243

kamcma opened this issue Jul 28, 2022 · 3 comments · Fixed by #244

Comments

@kamcma
Copy link

kamcma commented Jul 28, 2022

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?

@MaxDesiatov
Copy link
Collaborator

MaxDesiatov commented Jul 31, 2022

@kamcma I've renamed the protocol to XMLDecodableSequence and made it public in #244, does that resolve your issue?

@kamcma
Copy link
Author

kamcma commented Jul 31, 2022

It does. Thanks.

@MaxDesiatov
Copy link
Collaborator

MaxDesiatov commented Jul 31, 2022

Great, I've tagged 0.14.0 which includes this change. Thanks for reporting the issue!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants