-
-
Notifications
You must be signed in to change notification settings - Fork 774
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
Capture other unrecognized fields #941
Comments
I like this idea and might be able to submit a PR for it, but I have a few questions:
|
|
I personally would prefer naming this something more verbose like |
@dtolnay Right now, I only see |
No there is not. I guess make it an error. |
This is starting to feel suspiciously similar to #119, in that it will allow reading and writing of keys from a nested object into a flat namespace in the serialized object. If we had Assuming this is still desirable, I'm starting to wonder if this should be a container-level attribute rather than a field-level attribute. #[derive(Serialize, Deserialize)]
#[serde(unknown_fields_in="other")]
pub struct Sample {
hello: String,
other: HashMap<String, Value>
} The user experience is similar: The duplicated identifier is unfortunate, but semantically I think it's a little clearer. From an implementation perspective, a container-level approach would be marginally simpler:
|
I'm interested in developing this feature, but I haven't dug into the serde codebase at all up until this point. Is it a "beginner" issue? If so and with a little guidance I'd like to take this one up. |
Yes, go for it! |
@dtolnay can you give me some pointers - like if the field or container level attribute is preferred |
I would recommend handwriting some Deserialize impls for this and testing them against some JSON inputs to make sure they capture unrecognized fields the way you expected. I would recommend doing this before diving into attributes and proc macro stuff. |
@dtolnay okay thanks I'll see how far I get with this and if I make progress I'll share it here. |
I added a somewhat working PR for this now: #1178 I went with @TedDriggs's suggestion of a container attribute. However I do think that flattening and this feature cannot be merged for now. The reason for this is that in order to serialize unknown fields one needs to go through the map serialization system because of lifetime issues. As a result of that I decided to add a special mode to structs where structs are serialized into maps all the time and if that mode is on, deserialization into an For JSON it does not make a difference currently as the only thing you lose from this is the type name on the way to the serializer but JSON never used this anyways. |
I think this issue is subset of #119 ? Probably worth considering forwards compatibility with solution for that issue. |
@Marwes i do not believe that with the current traits flattening can be combined with capturing leftover data for the reasons outlined above. At least the |
@mitsuhiko I were thinking about that but it might be possible to have a // OK
#[serde(unknown_fields_into="other")]
struct S {
field: String,
other: T,
}
struct T {
field1: i32,
field2: i32,
}
// ERROR
#[serde(unknown_fields_into="other")]
struct S {
field: String,
other: HashMap<String, i32>,
} Failing that, the traits could be extended with alternative methods without the 'static lifetimes. These methods would only be called if the |
@Marwes i don't think forwarding unknown fields into anything other than a container that can eat anything makes sense. That would be a full overlap to a potential future flatten feature. |
I would prefer to combine this with #119. |
@dtolnay would all flatten downgrade into map serialization or only if flatten is used on a non struct? |
Flatten would always downgrade your struct into a map. In practice people only care about JSON anyway. |
@dtolnay doesn't your solution require that data in |
Yes. I think that should be okay because in the case where there is more than one flatten field it will pretty much always be 0 or more structs/enums within which we ignore unrecognized fields by default, followed by 0 or 1 final catch-all map. The DeserializeMapAdapter will need to remove entries from the input vec that are deserialized at each step, and keep only the ones that are deserialized with deserialize_ignored_any. That way the final catch-all only sees fields that have not been claimed by any previous flattened field. |
That means though if |
Like this? #[derive(Serialize, Deserialize)]
struct S {
s: String,
#[serde(flatten)]
other: u32,
} I would expect that to not work. |
More like HashMap<String, u32>
…On Wed, Mar 14, 2018 at 8:30 PM David Tolnay ***@***.***> wrote:
Like this?
#[derive(Serialize, Deserialize)]struct S {
s: String,
#[serde(flatten)]
other: u32,
}
I would expect that to not work.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#941 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAAc5G83WfPNR42ga2XUz--048VD3KzEks5teW_ugaJpZM4Nhtqo>
.
|
|
I see what I missed: that your sketch deserializes into the internal |
Implemented in #1179, released in Serde 1.0.34. #[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
use std::collections::BTreeMap as Map;
use serde_json::Value;
#[derive(Serialize, Deserialize)]
struct S {
a: u32,
b: String,
#[serde(flatten)]
other: Map<String, Value>,
}
fn main() {
let s = S {
a: 0,
b: "".to_owned(),
other: {
let mut map = Map::new();
map.insert("c".to_owned(), Value::Bool(true));
map
},
};
println!("{}", serde_json::to_string(&s).unwrap());
} |
Slightly simpler, but same idea working well for me as a debug with no other #[serde(flatten)]
other: serde_json::Value, On a tangent; this confirmed for me the way that serde parsing consumes as it flattens. I'm kinda curious if there's a way to control that somewhat. For example I have another place with two structs I would like nested, who each both get a copy of the parent id, for example. If the parser consumes the id for the first |
fix(toml): Provide a way to show unused manifest keys for dependencies Dependencies have not been able to show unused manifest keys for some time, this problem partially resulted in #11329. This problem is caused by having an `enum` when deserializing. To get around this you can use: ```rust #[serde(flatten)] other: BTreeMap<String, toml::Value>, ``` This collects any unused keys into `other` that can later be used to show warnings. This idea was suggested in a thread I cannot find but is mentioned in [serde#941](serde-rs/serde#941).
Is there any other way to achieve this without using serde_json? |
If you only concerned about |
Yes, because I'm not currently using JSON for anything, it was more s debugging tool, so serde value will probably suffice if it allows me to inspect the captured values |
This would deserialize into
other
containing "c" => true.The text was updated successfully, but these errors were encountered: