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

Add a default catchall case to enum to allow parsing elements with an unknown name #146

Closed
lovasoa opened this issue Jan 27, 2021 · 3 comments

Comments

@lovasoa
Copy link

lovasoa commented Jan 27, 2021

Let's say I have an xml document with the following form :

<root>
  <item1 />
  <item2 />
   ...
  <item999 />
</root>

and I am only interested in elements of type item1 and item2.

Currently, there is no good way to parse such a document with serde-xml-rs.

Try 1

I can write the following

struct Root {
   item1: Vec<Item1>,
   item2: Vec<Item2>,
}

Unfortunately, it won't work with documents such as

<root>
  <item1 />
  <item2 />
  <item1 />
</root>

because of #55 which has been a open issue since 2017

Try 2

I can write

struct Root {
    #[serde(rename = "$value")]
    items: Vec<Item>,
}


#[serde(rename_all = "lowercase")]
enum Item {
    Item1(Item1Props),
    Item2(Item2Props),
    Item3,
    ...
    Item999
}

but this requires me to know ahead of time all the possible tags that can occur in addition of <item1> and <item2>, which I don't.

Possible solution

I would like to add a way to parse all unknown tags in a single variant, like this

#[serde(rename_all = "lowercase")]
enum Item {
    Item1(Item1Props),
    Item2(Item2Props),
    #serde(rename = "$other")
    Other
}

This follows the same principle as the existing $value, but allows parsing any incoming tag without error.

For instance

<root> <item1 /> <item999 /> <item1 /> </root>

would be parsed as

Root { items : vec![ Item1{}, Other, Item1{} ] }

Is this issue clear enough ? Would you be interested in a PR implementing that ?

@lovasoa
Copy link
Author

lovasoa commented Jan 27, 2021

Reading more documentation, I see that serde has a #[serde(other)] enum attribute, but it works only on unit enum variants, and this crate refuses to deserialize xml tags with attributes or children to unit variants.

@lovasoa
Copy link
Author

lovasoa commented Jan 28, 2021

The best I found at the moment is

#[derive(Deserialize, Debug)]
#[serde(rename_all = "lowercase")]
enum ItemOrOther {
    Item1,
    Item2,
    #[serde(other, deserialize_with = "deserialize_ignore_any")]
    Other,
}

fn deserialize_ignore_any<'de, D: Deserializer<'de>>(deserializer: D) -> Result<(), D::Error> {
    serde::de::IgnoredAny::deserialize(deserializer)?;
    Ok(())
}

@punkstarman
Copy link
Collaborator

Thank you @lovasoa for your detailed info.

I am closing this because :

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

2 participants