diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index ae7a9f97..acc1086b 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -1716,8 +1716,7 @@ fn deserialize_generated_identifier( let (ignore_variant, fallthrough) = if cattrs.has_flatten() { let ignore_variant = quote!(__other(_serde::private::de::Content<'de>),); - let fallthrough = quote!(_serde::export::Ok(__Field::__other( - _serde::private::de::Content::String(__value.to_string())))); + let fallthrough = quote!(_serde::export::Ok(__Field::__other(__value))); (Some(ignore_variant), Some(fallthrough)) } else if is_variant || cattrs.deny_unknown_fields() { (None, None) @@ -1887,8 +1886,86 @@ fn deserialize_identifier( let variant_indices = 0u64..; let fallthrough_msg = format!("{} index 0 <= i < {}", index_expecting, fields.len()); - let visit_index = if collect_other_fields { - None + let visit_other = if collect_other_fields { + Some(quote! { + fn visit_bool<__E>(self, __value: bool) -> Result + where __E: _serde::de::Error + { + Ok(__Field::__other(_serde::private::de::Content::Bool(__value))) + } + + fn visit_i8<__E>(self, __value: i8) -> Result + where __E: _serde::de::Error + { + Ok(__Field::__other(_serde::private::de::Content::I8(__value))) + } + + fn visit_i16<__E>(self, __value: i16) -> Result + where __E: _serde::de::Error + { + Ok(__Field::__other(_serde::private::de::Content::I16(__value))) + } + + fn visit_i32<__E>(self, __value: i32) -> Result + where __E: _serde::de::Error + { + Ok(__Field::__other(_serde::private::de::Content::I32(__value))) + } + + fn visit_i64<__E>(self, __value: i64) -> Result + where __E: _serde::de::Error + { + Ok(__Field::__other(_serde::private::de::Content::I64(__value))) + } + + fn visit_u8<__E>(self, __value: u8) -> Result + where __E: _serde::de::Error + { + Ok(__Field::__other(_serde::private::de::Content::U8(__value))) + } + + fn visit_u16<__E>(self, __value: u16) -> Result + where __E: _serde::de::Error + { + Ok(__Field::__other(_serde::private::de::Content::U16(__value))) + } + + fn visit_u32<__E>(self, __value: u32) -> Result + where __E: _serde::de::Error + { + Ok(__Field::__other(_serde::private::de::Content::U32(__value))) + } + + fn visit_u64<__E>(self, __value: u64) -> Result + where __E: _serde::de::Error + { + Ok(__Field::__other(_serde::private::de::Content::U64(__value))) + } + + fn visit_f32<__E>(self, __value: f32) -> Result + where __E: _serde::de::Error + { + Ok(__Field::__other(_serde::private::de::Content::F32(__value))) + } + + fn visit_f64<__E>(self, __value: f64) -> Result + where __E: _serde::de::Error + { + Ok(__Field::__other(_serde::private::de::Content::F64(__value))) + } + + fn visit_char<__E>(self, __value: char) -> Result + where __E: _serde::de::Error + { + Ok(__Field::__other(_serde::private::de::Content::Char(__value))) + } + + fn visit_unit<__E>(self) -> Result + where __E: _serde::de::Error + { + Ok(__Field::__other(_serde::private::de::Content::Unit)) + } + }) } else { Some(quote! { fn visit_u64<__E>(self, __value: u64) -> _serde::export::Result @@ -1906,13 +1983,25 @@ fn deserialize_identifier( }) }; - let bytes_to_str = if fallthrough.is_some() && !collect_other_fields { + let bytes_to_str = if fallthrough.is_some() || collect_other_fields { None } else { - let conversion = quote! { + Some(quote! { let __value = &_serde::export::from_utf8_lossy(__value); - }; - Some(conversion) + }) + }; + + let (value_as_str_content, value_as_bytes_content) = if !collect_other_fields { + (None, None) + } else { + ( + Some(quote! { + let __value = _serde::private::de::Content::String(__value.to_string()); + }), + Some(quote! { + let __value = _serde::private::de::Content::ByteBuf(__value.to_vec()); + }) + ) }; let fallthrough_arm = if let Some(fallthrough) = fallthrough { @@ -1932,7 +2021,7 @@ fn deserialize_identifier( _serde::export::Formatter::write_str(formatter, #expecting) } - #visit_index + #visit_other fn visit_str<__E>(self, __value: &str) -> _serde::export::Result where __E: _serde::de::Error @@ -1941,7 +2030,10 @@ fn deserialize_identifier( #( #field_strs => _serde::export::Ok(#constructors), )* - _ => #fallthrough_arm + _ => { + #value_as_str_content + #fallthrough_arm + } } } @@ -1954,6 +2046,7 @@ fn deserialize_identifier( )* _ => { #bytes_to_str + #value_as_bytes_content #fallthrough_arm } } diff --git a/serde_derive/src/lib.rs b/serde_derive/src/lib.rs index 5cc2e1f8..faf0f1af 100644 --- a/serde_derive/src/lib.rs +++ b/serde_derive/src/lib.rs @@ -26,7 +26,7 @@ #![cfg_attr(feature = "cargo-clippy", allow(enum_variant_names, redundant_field_names, too_many_arguments, used_underscore_binding))] // The `quote!` macro requires deep recursion. -#![recursion_limit = "256"] +#![recursion_limit = "512"] #[macro_use] extern crate quote; diff --git a/test_suite/tests/test_annotations.rs b/test_suite/tests/test_annotations.rs index 1fd449d4..7ff073cf 100644 --- a/test_suite/tests/test_annotations.rs +++ b/test_suite/tests/test_annotations.rs @@ -1670,3 +1670,30 @@ fn test_flatten_unsupported_type() { "can only flatten structs and maps", ); } + +#[test] +fn test_non_string_keys() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct TestStruct { + name: String, + age: u32, + #[serde(flatten)] + mapping: HashMap, + } + + let mut mapping = HashMap::new(); + mapping.insert(0, 42); + assert_tokens( + &TestStruct { name: "peter".into(), age: 3, mapping }, + &[ + Token::Map { len: None }, + Token::Str("name"), + Token::Str("peter"), + Token::Str("age"), + Token::U32(3), + Token::U32(0), + Token::U32(42), + Token::MapEnd, + ], + ); +}