-
-
Notifications
You must be signed in to change notification settings - Fork 778
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
Add ability to deserialize enums from SeqAccessDeserializer
#2445
base: master
Are you sure you want to change the base?
Conversation
SeqAccessDeserialzier
SeqAccessDeserializer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR!
I am not convinced that this is the behavior that would be expected by users. As far as I can tell the motivation explained in the PR description is to make the following:
let deserializer = SeqAccessDeserializer::new(seq);
behave more like the following:
let rest = SeqAccessDeserializer::new(seq);
let content = try!(Content::deserialize(rest));
let deserializer = ContentDeserializer::<A::Error>::new(content);
But I tried that code, and ContentDeserializer
also does not deserialize enums from sequences. The current SeqAccessDeserializer
is already consistent with this.
use serde::de::value::SeqAccessDeserializer;
use serde::de::{Deserialize, Deserializer, SeqAccess, Visitor};
use serde_derive::Deserialize;
use std::fmt;
#[derive(Deserialize, Debug)]
enum Enum {
Variant(usize),
}
struct MyVisitor;
impl<'de> Visitor<'de> for MyVisitor {
type Value = Enum;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("repro")
}
fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let rest = SeqAccessDeserializer::new(seq);
let content = serde::__private::de::Content::deserialize(rest).unwrap();
let deserializer = serde::__private::de::ContentDeserializer::<A::Error>::new(content);
Enum::deserialize(deserializer)
}
}
fn main() {
let mut de = serde_json::Deserializer::from_str(r#" ["Variant", 1] "#);
let e = de.deserialize_any(MyVisitor).unwrap();
println!("{:?}", e);
}
I'm left unclear about what the motivation for this PR is.
I cannot remember exactly, why I decided to make |
I remembered the chain of reasoning. To make sure #1922 doesn't break existing behavior, I decided to add the missing tests. One of the tests is the deserialization of #[derive(Deserialize)]
enum ExternallyTagged {
Unit,
}
#[derive(Deserialize)]
#[serde(tag = "tag")]
enum InternallyTagged {
StructWithEnum { f: ExternallyTagged },
} Internally tagged enums can be represented either as maps or as sequences: [
{ "tag": "StructWithEnum", "f": "Unit" },
["StructWithEnum", "Unit"],
] You can make sure that on playground. Currently internally tagged enums always use bufferisation and Lines 1399 to 1406 in 3aca38d
After optimisation in #1922 in the optimized case Lines 853 to 894 in 3aca38d
Without changes in this PR the test from playground failed for #1922 (I've put it to serde's
Then I realized that deserialization tests of externally tagged from |
(review this commit with "ignore whitespace changes" option on)
The new message is more logical, because actually map *is* expected, but it should contain at least one entry
failures (6): access_to_enum::seq::empty_seq access_to_enum::seq::newtype access_to_enum::seq::struct_ access_to_enum::seq::tuple access_to_enum::seq::unit access_to_enum::seq::wrong_tag
…alizer Fixes all tests
Motivation of this PR is following: when I rebase #1922 I noticed, that test, introduced in the first commit of #2303 failed after the change. That failure appeared after changing
to
i. e. after removing intermediate bufferisation step. Because code for the
MapAccessDeserializer
is the same and it does not suffer from such problem, I checked the difference and realized, thatMapAccessDeserializer
has a special handling fo theDeserializer::deserialize_enum
hint:serde/serde/src/de/value.rs
Lines 1474 to 1484 in 25381be
which is missing for the
SeqAccessDeserializer
.Because
SeqAccessDeserializer
is used in such context whendeserialize_enum
could be requested from it, I think, it should provide the special implementation for that.This implementation is following: enum is represented as:
Additionally, this PR:
MapAccessDeserializer
frominvalid type: map, expected enum
toinvalid length: 0, expected enum
, because actually map is expected, but it should not be emptyDeserializer::deserialize_enum
forMapAccessDeserializer
andSeqAccessDeserializer