Open Eitot opened 5 years ago
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 🤔
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.
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)
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.
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. 👍
This doesn't sound worth it, it's not that arduous to define the whole structure.
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:
To get to the
title
node, I need to implement a type for thechannel
node as well: