Deserialising from toml adds extra double quotes. Can I get rid of them at the time of deserializing, or should I trim it explicitly later? Thanks

use std::{sync::Arc, collections::HashMap};

use serde::{Deserialize, Deserializer};
use toml::{self, Value}; // 0.5.9

fn main() {
    let toml_string = r#"[foobar]
arg = "foo"
name = "foo"
attr = [["foo", "bar"], ["baz", "abc"]]
name = "another_foo"
let foo: Custom = toml::from_str(toml_string).unwrap();
println!("Foo: {:?}", foo);

#[derive(Debug, Deserialize)]
pub struct Custom {
    foobar: FooBar 
#[derive(Debug, Deserialize)]
pub struct FooBar {
    arg: String,
    #[serde(deserialize_with = "parse_skip")]
    skip: Option<Arc<Vec<Skip>>>,
#[derive(Debug, Default)]
pub struct Skip {
    pub name: String,
    pub attr: Option<Arc<HashMap<String, String>>>,

fn parse_skip<'de, D>(deserializer: D) -> Result<Option<Arc<Vec<Skip>>>, D::Error>
    D: Deserializer<'de>,
    let mut skips: Vec<Skip> = Vec::new();
    let vals: Vec<HashMap<&str, Value>> = Deserialize::deserialize(deserializer)?;
    for val in vals.into_iter() {
        let mut skip = Skip {
        for (k, v) in val.into_iter() {
            if k.eq("name") {
       = v.to_string();
            if k.eq("attr") && v.is_array() {
                let labels = v
                    .filter(|l| l.len() == 2)
                    .map(|f| {
                        (f[0].to_string(), f[1].to_string())
                    .collect::<HashMap<String, String>>();
                skip.attr = Some(Arc::new(labels));




arg = "foo"
name = "foo"
attr = [["foo", "bar"], ["baz", "abc"]]
name = "another_foo"
Foo: Custom { foobar: FooBar { arg: "foo", skip: Some([Skip { name: "\"foo\"", attr: Some({"[\"foo\", \"bar\"]": "[\"baz\", \"abc\"]"}) }, Skip { name: "\"another_foo\"", attr: None }]) } }


   Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 1.89s
     Running `target/debug/playground`

Is it possible to get rid of the extra double quotes

name: "\"foo\""

and serialize it as

name: "foo"

Thank you.

It's your own deserializer function that adds the quotes.

fn main() {
    println!("{}", Value::String("test".to_string()));

toml::Value's Display impl (which is used by to_string) outputs the value as it would appear in a toml file, in this case the string foo becomes "foo". You should use match or Value::as_str instead.


Thank you @Heliozoa . However, my type inside the hashmap is a String, not &str and Value::as_str returns an &str, so wouldn't that require me to use to_string(), to_owned() or format!() to clone it as a String?

You should probably use .as_str().to_owned(). .to_string() uses the Display impl of Value to get the value, which prints out toml. .as_str() extracts an &str and then .to_owned() turns it into an owned String.


The 30 000-foot view question is: why are you deserializing into a Value in the first place? Your data clearly has a schema (it's statically typed), so you should be deserializing directly into your own domain objects instead of the toml Value type.


Thank you. The reason for deserializing it to a Value is because both are different types

"attr": Array()


"name": String()

or am I'm doing it wrong here?

You can deserialize into structs with different types of fields. In fact you are doing just that implicitly with all of the other #[derive(Deserialize)] impls. What are you trying to achieve that doesn't work out of the box with the derived, strongly-typed impls?

1 Like

Thank you. Let me try that and I will update the progress here.

to_owned from as_str was producing the same output, but using write! macro to create a String from the &str returned by as_str did the trick.
Thank you.

There must be something else you are overlooking here. <&str>::to_owned() definitely does not add any quotes on its own.


I also mentioned match, because Value is a regular enum where the String variant contains the string: Value in toml::value - Rust
So you can use match (or if let) to get the string out of it

if let Value::String(s) = value {
match value {
    Value::String(s) => println!("{s}"),
    _ => panic!("something else")

You're right. I was exhausted and I didn't know what I was writing. Started fresh, used match to cleanly parse each enums from toml::Value and yes, to_owned() doesn't add quotes. Everyone here were of great help. Thank you.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.