Skip to content

Commit bf5533a

Browse files
committed
Deserialization of tuple structs and IgnoredAny
The derived Deserialize implementation expects to be able to generate tuple structs from arrays, so this implements deserialize_tuple_struct to delegate to deserialize_seq. Also implements deserialize_ignored_any so that Deserializers can ignore fields of objects that they don’t care about. Includes some comments explaining why certain deserialize methods are unimplemented (I chose to use comments rather than passing a string to unreachable! so that the strings wouldn’t end up in the binary). Also adds a "CustomError" enum so that errors generated from the derive macro don’t cause a panic. (And includes a note that maybe we could put a heapless String there.)
1 parent f2481c1 commit bf5533a

File tree

2 files changed

+111
-7
lines changed

2 files changed

+111
-7
lines changed

src/de/map.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,10 +289,12 @@ impl<'de, 'a> de::Deserializer<'de> for MapKey<'a, 'de> {
289289
self.deserialize_str(visitor)
290290
}
291291

292-
fn deserialize_ignored_any<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
292+
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
293293
where
294294
V: Visitor<'de>,
295295
{
296-
unreachable!()
296+
// Even if we’re ignoring the contents of the map, we still need to
297+
// deserialize the string here in order to chomp the key’s characters.
298+
self.deserialize_str(visitor)
297299
}
298300
}

src/de/mod.rs

Lines changed: 107 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ pub enum Error {
6363
/// JSON has a comma after the last value in an array or map.
6464
TrailingComma,
6565

66+
/// Error with a custom message that we had to discard.
67+
CustomError,
68+
6669
#[doc(hidden)]
6770
__Extensible,
6871
}
@@ -276,6 +279,7 @@ macro_rules! deserialize_signed {
276279
impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
277280
type Error = Error;
278281

282+
/// Unsupported. Can’t parse a value without knowing its expected type.
279283
fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value>
280284
where
281285
V: Visitor<'de>,
@@ -396,20 +400,23 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
396400
}
397401
}
398402

403+
/// Unsupported. String is not available in no-std.
399404
fn deserialize_string<V>(self, _visitor: V) -> Result<V::Value>
400405
where
401406
V: Visitor<'de>,
402407
{
403408
unreachable!()
404409
}
405410

411+
/// Unsupported
406412
fn deserialize_bytes<V>(self, _visitor: V) -> Result<V::Value>
407413
where
408414
V: Visitor<'de>,
409415
{
410416
unreachable!()
411417
}
412418

419+
/// Unsupported
413420
fn deserialize_byte_buf<V>(self, _visitor: V) -> Result<V::Value>
414421
where
415422
V: Visitor<'de>,
@@ -431,20 +438,23 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
431438
}
432439
}
433440

441+
/// Unsupported. Use a more specific deserialize_* method
434442
fn deserialize_unit<V>(self, _visitor: V) -> Result<V::Value>
435443
where
436444
V: Visitor<'de>,
437445
{
438446
unreachable!()
439447
}
440448

449+
/// Unsupported. Use a more specific deserialize_* method
441450
fn deserialize_unit_struct<V>(self, _name: &'static str, _visitor: V) -> Result<V::Value>
442451
where
443452
V: Visitor<'de>,
444453
{
445454
unreachable!()
446455
}
447456

457+
/// Unsupported. We can’t parse newtypes because we don’t know the underlying type.
448458
fn deserialize_newtype_struct<V>(self, _name: &'static str, _visitor: V) -> Result<V::Value>
449459
where
450460
V: Visitor<'de>,
@@ -480,14 +490,17 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
480490
self,
481491
_name: &'static str,
482492
_len: usize,
483-
_visitor: V,
493+
visitor: V,
484494
) -> Result<V::Value>
485495
where
486496
V: Visitor<'de>,
487497
{
488-
unreachable!()
498+
self.deserialize_seq(visitor)
489499
}
490500

501+
/// Unsupported. Can’t make an arbitrary-sized map in no-std. Use a struct with a
502+
/// known format, or implement a custom map deserializer / visitor:
503+
/// https://serde.rs/deserialize-map.html
491504
fn deserialize_map<V>(self, _visitor: V) -> Result<V::Value>
492505
where
493506
V: Visitor<'de>,
@@ -541,20 +554,45 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
541554
self.deserialize_str(visitor)
542555
}
543556

544-
fn deserialize_ignored_any<V>(self, _visitor: V) -> Result<V::Value>
557+
/// Used to throw out fields from JSON objects that we don’t want to
558+
/// keep in our structs.
559+
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value>
545560
where
546561
V: Visitor<'de>,
547562
{
548-
unreachable!()
563+
match self.parse_whitespace().ok_or(Error::EofWhileParsingValue)? {
564+
b'"' => self.deserialize_str(visitor),
565+
b'[' => self.deserialize_seq(visitor),
566+
b'{' => self.deserialize_struct("ignored", &[], visitor),
567+
b',' | b'}' | b']' => Err(Error::ExpectedSomeValue),
568+
// If it’s something else then we chomp until we get to an end delimiter.
569+
// This does technically allow for illegal JSON since we’re just ignoring
570+
// characters rather than parsing them.
571+
_ => loop {
572+
match self.peek() {
573+
// The visitor is expected to be UnknownAny’s visitor, which
574+
// implements visit_unit to return its unit Ok result.
575+
Some(b',') | Some(b'}') | Some(b']') => break visitor.visit_unit(),
576+
Some(_) => self.eat_char(),
577+
None => break Err(Error::EofWhileParsingString),
578+
}
579+
},
580+
}
549581
}
550582
}
551583

552584
impl de::Error for Error {
585+
// We can’t alloc a String to save the msg in, so we have this less-than-useful
586+
// error as better than panicking with unreachable!. These errors can arise from
587+
// derive, such as "not enough elements in a tuple" and "missing required field".
588+
//
589+
// TODO: consider using a heapless::String to save the first n characters of this
590+
// message.
553591
fn custom<T>(_msg: T) -> Self
554592
where
555593
T: fmt::Display,
556594
{
557-
unreachable!()
595+
Error::CustomError
558596
}
559597
}
560598

@@ -594,6 +632,7 @@ impl fmt::Display for Error {
594632
value."
595633
}
596634
Error::TrailingComma => "JSON has a comma after the last value in an array or map.",
635+
Error::CustomError => "JSON does not match deserializer’s expected format.",
597636
_ => "Invalid JSON",
598637
}
599638
)
@@ -756,6 +795,69 @@ mod tests {
756795
assert!(crate::from_str::<Temperature>(r#"{ "temperature": -1 }"#).is_err());
757796
}
758797

798+
#[test]
799+
fn struct_tuple() {
800+
#[derive(Debug, Deserialize, PartialEq)]
801+
struct Xy(i8, i8);
802+
803+
assert_eq!(crate::from_str(r#"[10, 20]"#), Ok(Xy(10, 20)));
804+
assert_eq!(crate::from_str(r#"[10, -20]"#), Ok(Xy(10, -20)));
805+
806+
// wrong number of args
807+
assert_eq!(crate::from_str::<Xy>(r#"[10]"#), Err(crate::de::Error::CustomError));
808+
assert_eq!(crate::from_str::<Xy>(r#"[10, 20, 30]"#), Err(crate::de::Error::TrailingCharacters));
809+
}
810+
811+
#[test]
812+
fn ignoring_extra_fields() {
813+
#[derive(Debug, Deserialize, PartialEq)]
814+
struct Temperature {
815+
temperature: u8,
816+
}
817+
818+
assert_eq!(
819+
crate::from_str(r#"{ "temperature": 20, "high": 80, "low": -10, "updated": true }"#),
820+
Ok(Temperature { temperature: 20 })
821+
);
822+
823+
assert_eq!(
824+
crate::from_str(
825+
r#"{ "temperature": 20, "conditions": "windy", "forecast": "cloudy" }"#
826+
),
827+
Ok(Temperature { temperature: 20 })
828+
);
829+
830+
assert_eq!(
831+
crate::from_str(r#"{ "temperature": 20, "hourly_conditions": ["windy", "rainy"] }"#),
832+
Ok(Temperature { temperature: 20 })
833+
);
834+
835+
assert_eq!(
836+
crate::from_str(r#"{ "temperature": 20, "source": { "station": "dock", "sensors": ["front", "back"] } }"#),
837+
Ok(Temperature { temperature: 20 })
838+
);
839+
840+
assert_eq!(
841+
crate::from_str(r#"{ "temperature": 20, "invalid": this-is-ignored }"#),
842+
Ok(Temperature { temperature: 20 })
843+
);
844+
845+
assert_eq!(
846+
crate::from_str::<Temperature>(r#"{ "temperature": 20, "broken": }"#),
847+
Err(crate::de::Error::ExpectedSomeValue)
848+
);
849+
850+
assert_eq!(
851+
crate::from_str::<Temperature>(r#"{ "temperature": 20, "broken": [ }"#),
852+
Err(crate::de::Error::ExpectedSomeValue)
853+
);
854+
855+
assert_eq!(
856+
crate::from_str::<Temperature>(r#"{ "temperature": 20, "broken": ] }"#),
857+
Err(crate::de::Error::ExpectedSomeValue)
858+
);
859+
}
860+
759861
// See https://iot.mozilla.org/wot/#thing-resource
760862
#[test]
761863
#[ignore]

0 commit comments

Comments
 (0)