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

Skip intermediate nodes #365

Closed
pronebird opened this issue Feb 20, 2022 · 4 comments
Closed

Skip intermediate nodes #365

pronebird opened this issue Feb 20, 2022 · 4 comments
Labels
question serde Issues related to mapping from Rust types to XML

Comments

@pronebird
Copy link

pronebird commented Feb 20, 2022

Hi,

I have XML that has container/wrapper types around collections of nodes and I'd like to skip them when parsing, for example:

<Response>
  <Entities count="2">
      <Entity />
      <Entity />
  </Entities>
</Response>

i.e it works when I provide a struct Entities as a container that holds children:

#[derive(Deserialize, Debug)]
pub struct Response {
    #[serde(rename = "Entities")]
    entities: Entities,
}

#[derive(Deserialize, Debug)]
struct Entities {
    count: u32,

    #[serde(rename = "Entity")]
    children: Vec<Entity>,
}

#[derive(Deserialize, Debug)]
struct Entity {
   ///...
}

But what I would really like to do is to eliminate the struct Entities and have a Vec<Entity> instead, i.e:

#[derive(Deserialize, Debug)]
pub struct Response {
    #[serde(rename = "Entities")]
    entities: Vec<Entity>,
}

How would one achieve that?

Thank you.

@ImJeremyHe
Copy link

I guess the only way to do this is to implement your own Deserialize.

@Mingun
Copy link
Collaborator

Mingun commented May 7, 2022

You can use deserialize_with:

fn unwrap_list<'de, D>(deserializer: D) -> Result<Vec<Entity>, D::Error> 
where
    D: Deserializer<'de>,
{
    Ok(Entities::deserialize(deserializer)?.children)
}

#[derive(Deserialize, Debug)]
pub struct Response {
    #[serde(rename = "Entities", deserialize_with = "unwrap_list")]
    entities: Vec<Entity>,
}

Example for JSON, but it also should work for quick-xml: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=79f164ac3654d4d9ab6826ff156ac937

@pronebird
Copy link
Author

Appreciate it! Thanks!

@Mingun Mingun added question serde Issues related to mapping from Rust types to XML labels May 21, 2022
@Mingun Mingun closed this as completed May 21, 2022
@RodogInfinite
Copy link

RodogInfinite commented May 28, 2022

I made this a bit more generic with a macro:

use serde::{Deserialize, Serialize, Deserializer};
use quick_xml::de::{from_str, DeError};


#[derive(Debug, Deserialize, Serialize)]
struct InnerNestedDetail {
    #[serde(rename="modification_date",default)]
    modification_date: String, // String just for example
    #[serde(rename="version",default)]
    version: f32,
    #[serde(rename="description",default)]
    description: String,
}


#[derive(Debug, Deserialize, Serialize)]
struct InnerNested{
   #[serde(rename = "Inner_Nested_Detail")]
   details: Vec<InnerNestedDetail>
}

macro_rules! unwrap_vec {
    ( $($name:ident,$outer_struct:ident,$field:ident,$inner_struct:ident)* ) => {
        $(          
             fn $name<'de,D>(deserializer:D) -> Result<Vec<$inner_struct>,D::Error>
             where
             D: Deserializer<'de>,
             {
                
                 Ok($outer_struct::deserialize(deserializer)?.$field)
             }
        )*
    }
}

unwrap_vec!(unwrap_inner_nested_vec,InnerNested,details,InnerNestedDetail);


#[derive(Debug, Deserialize, Serialize)]
pub struct OuterTag {
    identifier: String,
    version: f32,
    #[serde(rename = "Inner_Nested", deserialize_with = "unwrap_inner_nested_vec")] 
    inner_nested: Vec<InnerNestedDetail>, 
}

fn parse_xml(xml_string:&str) -> Result<OuterTag,DeError> {
    let test: OuterTag =  from_str(xml_string)?;
    Ok(test)
}

fn main(){
    let xml_string = "<Outer_Tag>
            <identifier>Some Identifier</identifier>
            <version>2.0</version>
            <Inner_Nested>
                <Inner_Nested_Detail>
                    <modification_date>2022-04-20</modification_date>
                    <version>1.0</version>
                    <description>Initial version.</description>
                </Inner_Nested_Detail>
                <Inner_Nested_Detail>
                    <modification_date>2022-05-20</modification_date>
                    <version>2.0</version>
                    <description>Modified version.</description>
                </Inner_Nested_Detail>
            </Inner_Nested>
        </Outer_Tag>";
    let result = parse_xml(xml_string).unwrap();

    println!("{:#?}",result);
    println!("\nInner Nested Details Vec:\n{:#?}",result.inner_nested);

}

I figured it would be easier to use a macro if you had multiple structures that needed this functionality. It could probably have a better name

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question serde Issues related to mapping from Rust types to XML
Projects
None yet
Development

No branches or pull requests

4 participants