RReverser / serde-xml-rs

xml-rs based deserializer for Serde (compatible with 1.0+)
https://crates.io/crates/serde-xml-rs
MIT License
272 stars 90 forks source link

Parsing XML with varying child elements #167

Closed benjaminSchilling33 closed 2 years ago

benjaminSchilling33 commented 3 years ago

I'm trying to deserialize webdav responses from a Nextcloud instance.

Currently the parsing failes due to the following error: UnexpectedToken { token: "EndElement", found: "Characters" }

I'm not 100% sure what causes this problem, but I would expect that it is caused by the following issue:

I receive the response as XML that contains an element that can have child elements, in this case prop with varying child elements, like the following.

How would you set up the structs with their fields and the corresponding serde annotations to parse this kind of data?

response
  - propstat
      -  prop
        - getlastmodified
        - resourcetype
        - quoata-used-bytes
        - ...
      - status
response
  - propstat
      -  prop
        - getcontentlength
        - getcontenttype
      - status

Example XML:

[...]
    <d:response>
        <d:href>/remote.php/dav/files/username/</d:href>
        <d:propstat>
            <d:prop>
                <d:getlastmodified>Thu, 21 Oct 2021 13:16:55 GMT</d:getlastmodified>
                <d:resourcetype>
                    <d:collection/>
                </d:resourcetype>
                <d:quota-used-bytes>44515001276</d:quota-used-bytes>
                <d:quota-available-bytes>-3</d:quota-available-bytes>
                <d:getetag>"617168427206a"</d:getetag>
            </d:prop>
            <d:status>HTTP/1.1 200 OK</d:status>
        </d:propstat>
    </d:response>
    <d:response>
        <d:href>/remote.php/dav/files/username/directory/</d:href>
        <d:propstat>
            <d:prop>
                <d:getlastmodified>Sat, 08 Feb 2020 13:39:33 GMT</d:getlastmodified>
                <d:resourcetype>
                    <d:collection/>
                </d:resourcetype>
                <d:quota-used-bytes>1269879245</d:quota-used-bytes>
                <d:quota-available-bytes>-3</d:quota-available-bytes>
                <d:getetag>"5e3eba155a300"</d:getetag>
            </d:prop>
            <d:status>HTTP/1.1 200 OK</d:status>
        </d:propstat>
        <d:propstat>
            <d:prop>
                <d:getcontentlength/>
                <d:getcontenttype/>
            </d:prop>
            <d:status>HTTP/1.1 404 Not Found</d:status>
        </d:propstat>
    </d:response>
[...]

My current approach:


#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct Multiresponse {
    #[serde(rename = "response")]
    responses: Vec<Response>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Response {
     href: String,
     #[serde(rename = "propstat")]
     propstat: Vec<Propstat>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Propstat {
    status: String,
    #[serde(rename = "prop")]
    prop: Prop,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Prop {
    #[serde(default)]
    getlastmodified: String,
    #[serde(default)]
    getetag: String,
    #[serde(default)]
    getcontentlength: i64,
    #[serde(default)]
    getcontenttype: String,

    #[serde(default, rename(deserialize = "quota-used-bytes"))]
    quota_used_bytes: i64,

    #[serde(default, rename(deserialize = "quota-available-bytes"))]
    quota_available_bytes: i64,
}
1sra3l commented 2 years ago

Hey I'd use Option<whatever> for the items that are not always in every single prop Something like:

#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Prop {
    #[serde(default)]
    getlastmodified: Option<String>,
    #[serde(default)]
    getetag: Option<String>,
    #[serde(default)]
    getcontentlength: i64,
    #[serde(default)]
    getcontenttype: String,

    #[serde(default, rename(deserialize = "quota-used-bytes"))]
    quota_used_bytes: Option<i64>,

    #[serde(default, rename(deserialize = "quota-available-bytes"))]
    quota_available_bytes:Option<i64>,
}
benjaminSchilling33 commented 2 years ago

thanks, that helped!