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

Flatten + deserialize_with generates incorrect Deserialize code #1240

Closed
dtolnay opened this issue May 2, 2018 · 2 comments
Closed

Flatten + deserialize_with generates incorrect Deserialize code #1240

dtolnay opened this issue May 2, 2018 · 2 comments

Comments

@dtolnay
Copy link
Member

dtolnay commented May 2, 2018

The following flattened field needs to invoke our deserialize_with function, not Point::deserialize.

#[macro_use]
extern crate serde_derive;

extern crate serde;

use serde::Deserializer;

#[derive(Deserialize)]
struct Location {
    #[serde(flatten, deserialize_with = "de")]
    point: Point,
}

struct Point;

fn de<'de, D>(_deserializer: D) -> Result<Point, D::Error>
where
    D: Deserializer<'de>,
{
    unimplemented!()
}
error[E0277]: the trait bound `Point: serde::Deserialize<'_>` is not satisfied
 --> src/main.rs:8:10
  |
8 | #[derive(Deserialize)]
  |          ^^^^^^^^^^^ the trait `serde::Deserialize<'_>` is not implemented for `Point`
  |
  = note: required by `serde::Deserialize::deserialize`

Sub-issue of #1239.

@dtolnay
Copy link
Member Author

dtolnay commented May 2, 2018

Fixed in cb2b92f.

@Stargateur
Copy link

Seem related, playground.

use std::collections::HashMap;
use std::fmt;
use std::str::FromStr;

use serde; // 1.0.85
use serde::de::{self, Deserialize, MapAccess, Visitor}; // 1.0.85
use serde::Deserializer;
use serde_derive::Deserialize; // 1.0.85
use toml; // 0.4.10
use void::Void; // 1.0.2

#[derive(Debug)]
struct Obj {
    x: isize,
    y: String,
}

struct ObjVisitor;

#[derive(Debug, Deserialize)]
struct ObjAux {
    x: isize,
    y: String,
}

impl<'de> Visitor<'de> for ObjVisitor {
    type Value = Obj;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("string or map")
    }

    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        Ok(FromStr::from_str(value).unwrap())
    }

    fn visit_map<M>(self, visitor: M) -> Result<Self::Value, M::Error>
    where
        M: MapAccess<'de>,
    {
        let aux: ObjAux = Deserialize::deserialize(de::value::MapAccessDeserializer::new(visitor))?;
        Ok(Obj { x: aux.x, y: aux.y })
    }
}

impl<'de> Deserialize<'de> for Obj {
    fn deserialize<D>(deserializer: D) -> Result<Obj, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_any(ObjVisitor)
    }
}

impl FromStr for Obj {
    type Err = Void;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(Obj {
            x: 0,
            y: s.to_owned(),
        })
    }
}

#[derive(Debug, Deserialize)]
struct Simple {
    obj: Obj,
}

#[derive(Debug, Deserialize)]
struct InsideHashMap {
    objs: HashMap<String, Obj>,
}

fn main() {
    // Basic deserialization of Obj
    let toml = r#"
        x = 5
        y = "hello"
    "#;
    let obj: Obj = toml::from_str(toml).unwrap();
    println!("{:?}", obj);

    // Basic deserialization of Obj as a field in a struct
    let toml = r#"
        [obj]
        x = 5
        y = "hello"
    "#;
    let simple: Simple = toml::from_str(toml).unwrap();
    println!("{:?}", simple);

    // Basic deserialization of Obj as a field in a struct as a string or struct
    let toml = r#"
        obj = "hello"
    "#;
    let simple: Simple = toml::from_str(toml).unwrap();
    println!("{:?}", simple);

    // Deserialization of an Obj inside a HashMap
    let toml = r#"
        [objs]
        a = { x = 5, y = "hello" }
    "#;
    let working: InsideHashMap = toml::from_str(toml).unwrap();
    println!("{:?}", working);

    // Deserialization of Obj inside a HashMap field as a string or struct
    let toml = r#"
        [objs]
        a = "hello"
    "#;
    let not_working: InsideHashMap = toml::from_str(toml).unwrap();
    println!("{:?}", not_working);
}

Look like deserialise_with is not used in this case too.

Stackoverflow question

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

2 participants