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

Unable to serialize a struct that deserialization works for #654

Open
tatupesonen opened this issue Sep 20, 2023 · 4 comments
Open

Unable to serialize a struct that deserialization works for #654

tatupesonen opened this issue Sep 20, 2023 · 4 comments
Labels
bug serde Issues related to mapping from Rust types to XML

Comments

@tatupesonen
Copy link

I'm trying to serialize/deserialize some data that an API responds with. This is what the XML from the API looks like:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <soapenv:Body>
        <LoginResponse xmlns="urn:vim25">
             <returnval>
                <userName>username_here</userName>
                <loggedInAt>datetime_here</loggedInAt>
            </returnval>
        </LoginResponse>
    </soapenv:Body>
</soapenv:Envelope>

And this is my code:

#[derive(Deserialize, Debug, Serialize)]
#[serde(rename = "soapenv:Envelope")]
pub struct SoapResponse {
    #[serde(rename = "@xmlns:soapenc")]
    soapenc: Option<String>,
    #[serde(rename = "@xmlns:soapenv")]
    soapenv: Option<String>,
    #[serde(rename = "@xmlns:xsd")]
    xsd: Option<String>,
    #[serde(rename = "@xmlns:xsi")]
    xsi: Option<String>,
    #[serde(rename = "$value")]
    pub body: SoapBody,
}

#[derive(Deserialize, Debug, Serialize)]
pub struct SoapBody {
    #[serde(rename = "$value")]
    pub response_type: Response,
}

#[derive(Deserialize, Debug, Serialize)]
pub enum Response {
    #[serde(rename = "LoginResponse")]
    LoginResponse {
        #[serde(rename = "@xmlns")]
        xmlns: String,
        #[serde(rename = "$value")]
        returnval: LoginResponse,
    },
    #[serde(rename = "Other")]
    SomeOther {},
}

#[derive(Deserialize, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LoginResponse {
    logged_in_at: String,
    user_name: String,
}

Deserialization works fine. However, when I try to serialize a structure like this:

use quick_xml::se::to_string;

let thing = SoapResponse {
    soapenc: None,
    soapenv: None,
    xsd: None,
    xsi: None,
    body: {
        SoapBody {
            response_type: Response::LoginResponse {
                xmlns: "urn:vim25".into(),
                returnval: LoginResponse {
                    logged_in_at: "example".into(),
                    user_name: "example".into(),
                },
            },
        }
    },
};
let thing = to_string(&thing);
dbg!(&thing);

I get

 Err(
    Unsupported(
        "serialization of struct `SoapBody` is not supported in `$value` field",
    ),
)

What could be the reason that causes this?

Versions:

quick-xml 0.30.0 with features = ["serde", "serialize", "arbitrary", "serde-types", "overlapped-lists", "document-features", "encoding", "encoding_rs"]
Rust 1.72
@Mingun
Copy link
Collaborator

Mingun commented Sep 20, 2023

You do not need to rename body to $value here:

    #[serde(rename = "$value")]
    pub body: SoapBody,

Use usual rename, for example, #[serde(rename = "soapenv:Body")]. $value is required for enumerated types, but SoapBody is a struct.

It is possible to fix this behavior, although.

@Mingun Mingun added bug serde Issues related to mapping from Rust types to XML labels Sep 20, 2023
@tatupesonen
Copy link
Author

tatupesonen commented Sep 21, 2023

I changed the SoapResponse to this:

#[derive(Deserialize, Debug, Serialize)]
#[serde(rename = "soapenv:Envelope")]
pub struct SoapResponse {
    #[serde(rename = "@xmlns:soapenc")]
    soapenc: String,
    #[serde(rename = "@xmlns:soapenv")]
    soapenv: String,
    #[serde(rename = "@xmlns:xsd")]
    xsd: String,
    #[serde(rename = "@xmlns:xsi")]
    xsi: String,
    #[serde(rename = "soapenv:Body")]
    pub body: SoapBody,
}

and the SoapBody:

#[derive(Deserialize, Debug, Serialize)]
#[serde(rename = "soapenv:Body")]
pub struct SoapBody {
    #[serde(rename = "$value")]
    pub response_type: Response,
}

Serialization works fine now, but deserialization now doesn't work. Trying to use the same XML.

 Custom(
        "missing field `soapenv:Body`",
    ),

Edit: Interestingly, if I set the field to rename like this:

    #[serde(rename = "Body")]
    pub body: SoapBody,

And change the XML from <soapenv:Body> to <Body>, now both deserialization and serialization work fine.

@Mingun
Copy link
Collaborator

Mingun commented Sep 21, 2023

Yes, namespaced renames works only occasionally, in general we do not support namespaces in serde (de)serializer yet.

@SarguelUnda
Copy link

A trick that use to work for this case was :

#[serde(alias = "soapenv:Body", rename(serialize = "soapenv:Body"))]
pub struct SoapBody {
    #[serde(rename = "$value")]
    pub response_type: Response,
}

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

No branches or pull requests

3 participants