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

Specify starting node for decoding #71

Open
Eitot opened this issue Jan 31, 2019 · 6 comments
Open

Specify starting node for decoding #71

Eitot opened this issue Jan 31, 2019 · 6 comments

Comments

@Eitot
Copy link

Eitot commented Jan 31, 2019

Perhaps I am overlooking this, but I think it would be useful if the starting node could be specified in some way, perhaps via XPath. While trying to implement an RSS feed, I found that I either have to implement the parent node to get to the child nodes I am interested in or write my own init(from decoder: Decoder).

For example:

<?xml version="1.0"?>
<rss version="2.0">
  <channel>
    <title>Title</title>
…
  </channel>
</rss>

To get to the title node, I need to implement a type for the channel node as well:

struct RSSFeed: Decodable {
  let channel: Channel

  struct Channel: Decodable {
    let title: String
  }
}
@MaxDesiatov
Copy link
Collaborator

Hi @Eitot, thank you for the suggestions. This somewhat overlaps with #10 as far as I understand. We do have in mind a new API for resolving some of the problems with mapping different structures, but it doesn't cover this specific case. Maybe you could suggest some to way to tie that in? XPath is a very interesting idea, I wonder how difficult it would be to implement something like that 🤔

@MaxDesiatov
Copy link
Collaborator

MaxDesiatov commented Jan 31, 2019

I wonder if we could make CodingKeys handle proper XPath, this would probably need a flag like isXPathEnabled or something on XMLDecoder and XMLEncoder, so that there's no performance impact on types that don't use it.

struct RSSFeed: Decodable {
  let channel: String

  enum CodingKeys: String, CodingKey {
    case channel = "channel/title"
  }
}

CC @regexident as he has a lot of experience with Codable internals and I hope would be able to clarify if this XPath idea makes any sense.

@Eitot
Copy link
Author

Eitot commented Feb 24, 2019

To be honest, I have no idea how to implement this. JSONDecoder and PropertyListDecoder have the same shortcoming. They require an init(from decoder: Decoder) implementation that does way with the code generation. Depending on the size of the model, it could lead to a lot of boilerplate code. Your suggested solution won’t avoid this either.

I am looking for a solution that keeps the code generation intact, while allowing for a straightforward model declaration. Essentially, I just want to declare this:

struct RSSFeed: Decodable {
  let title: String
}

At the moment I have to implement a init(from decoder: Decoder) implementation like this:

struct RSSFeed: Decodable {
  let title: String

  private enum CodingKeys: String, CodingKey {
      case channel
      case title
  }

  init(from decoder: Decoder) throws {
      let channel = try decoder.container(keyedBy: CodingKeys.self)
      let values = try channel.nestedContainer(keyedBy: CodingKeys.self, forKey: .channel)
      title = try values.decode(String.self, forKey: .title)
  }
}

Bear in mind, this is just a simple example. RSS is of course a lot more extensive than this.

Perhaps it would be enough to add an optional startingNode property to XMLDecoder?

let decoder = XMLDecoder()
decoder.startingNode = "channel" // or "/rss/channel"
let feed = try decoder.decode(RSSFeed.self, from: data)

@MaxDesiatov
Copy link
Collaborator

Ok, I see. Thank you for clarifying @Eitot. My initial understanding was that you were looking for a way to "skip" elements in general at any stage during decoding, no just the initial element. Specifying just the starting node does make sense. In your case specifying "channel" would point to a node unambiguously, but for this to work in general case, XPath probably works better overall.

But it looks like a work around does exist if I understand correctly and it's not that problematic at the moment?

Unfortunately, we still have a few higher priority issues, the top one being ordered encoding. If you know there's a lightweight XPath implementation for Swift we could incorporate somehow, please let me know. This would probably make the starting node API much easier to implement.

@Eitot
Copy link
Author

Eitot commented Feb 24, 2019

Indeed, it is not a problem, just… inelegant. After all, this is how JSONDecoder works too. I’d be happy if this is implemented at some point. 👍

@mxcl
Copy link

mxcl commented Sep 6, 2019

This doesn't sound worth it, it's not that arduous to define the whole structure.

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

No branches or pull requests

3 participants