1use aws_smithy_schema::serde::SerdeError;
9use aws_smithy_schema::serde::ShapeDeserializer;
10use aws_smithy_schema::{Schema, ShapeType};
11use aws_smithy_types::{
12 BigDecimal, BigInteger, Blob, DateTime, DiscriminatedDocument, Document, DocumentSettings,
13 Number,
14};
15
16use crate::codec::JsonCodecSettings;
17use crate::deserialize::{json_token_iter, Token};
18
19use std::sync::Arc;
20
21pub(crate) const MAX_DESERIALIZE_DEPTH: u32 = 128;
25
26pub struct JsonDeserializer<'a> {
28 input: &'a [u8],
29 position: usize,
30 settings: Arc<JsonCodecSettings>,
31 depth: u32,
32}
33
34impl<'a> JsonDeserializer<'a> {
35 pub(crate) fn new(input: &'a [u8], settings: Arc<JsonCodecSettings>) -> Self {
37 Self {
38 input,
39 position: 0,
40 settings,
41 depth: 0,
42 }
43 }
44
45 pub fn position(&self) -> usize {
49 self.position
50 }
51
52 fn resolve_member<'s>(
54 &self,
55 schema: &'s Schema<'s>,
56 field_name: &str,
57 ) -> Option<&'s Schema<'s>> {
58 self.settings.field_to_member(schema, field_name)
59 }
60
61 fn remaining(&self) -> &[u8] {
62 &self.input[self.position..]
63 }
64
65 fn advance_by(&mut self, n: usize) {
66 self.position = (self.position + n).min(self.input.len());
67 }
68
69 fn parse_key(&mut self) -> Result<std::borrow::Cow<'a, str>, SerdeError> {
74 let start = self.position + 1; self.position += 1;
76 let input = self.input;
77 let remaining = &input[start..];
78 let mut i = 0;
79 let mut has_escapes = false;
80 let mut found_end = false;
81 while i < remaining.len() {
82 match remaining[i] {
83 b'"' => {
84 found_end = true;
85 break;
86 }
87 b'\\' => {
88 has_escapes = true;
89 i += 2;
90 }
91 0..=31 if self.settings.enforce_strictness => {
92 return Err(SerdeError::invalid_input("raw control character in string"));
93 }
94 _ => i += 1,
95 }
96 }
97 if !found_end {
98 return Err(SerdeError::invalid_input("unterminated string key"));
99 }
100 self.position = start + i + 1; let key_bytes = &input[start..start + i];
102 if has_escapes {
103 let raw = std::str::from_utf8(key_bytes)
104 .map_err(|e| SerdeError::invalid_input(e.to_string()))?;
105 Ok(std::borrow::Cow::Owned(
106 crate::escape::unescape_string(raw)
107 .map_err(|e| SerdeError::invalid_input(e.to_string()))?
108 .into_owned(),
109 ))
110 } else {
111 Ok(std::borrow::Cow::Borrowed(
112 std::str::from_utf8(key_bytes)
113 .map_err(|e| SerdeError::invalid_input(e.to_string()))?,
114 ))
115 }
116 }
117
118 fn is_absolute_shape_id(s: &str) -> bool {
130 let Some((namespace, shape_name)) = s.split_once('#') else {
131 return false;
132 };
133 !namespace.is_empty()
134 && !shape_name.is_empty()
135 && !shape_name.contains('#')
136 && !shape_name.contains('$')
137 && !namespace.contains('$')
138 }
139
140 fn is_relative_shape_name(s: &str) -> bool {
151 let mut bytes = s.bytes();
152 let Some(first) = bytes.next() else {
153 return false;
154 };
155 if !(first.is_ascii_alphabetic() || first == b'_') {
156 return false;
157 }
158 bytes.all(|b| b.is_ascii_alphanumeric() || b == b'_')
159 }
160
161 fn resolve_shape_id(s: &str, default_ns: Option<&str>) -> Option<String> {
167 if s.contains('#') {
168 if Self::is_absolute_shape_id(s) {
173 Some(s.to_owned())
174 } else {
175 None
176 }
177 } else {
178 match default_ns {
183 Some(ns) if Self::is_relative_shape_name(s) => Some(format!("{ns}#{s}")),
184 _ => None,
185 }
186 }
187 }
188}
189
190impl<'a> ShapeDeserializer for JsonDeserializer<'a> {
191 fn read_struct(
192 &mut self,
193 schema: &Schema<'_>,
194 consumer: &mut dyn FnMut(&Schema<'_>, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
195 ) -> Result<(), SerdeError> {
196 self.depth += 1;
197 if self.depth > self.settings.max_depth() {
198 return Err(SerdeError::custom("maximum nesting depth exceeded"));
199 }
200 self.skip_whitespace();
202 if self.remaining().is_empty() {
203 if self.settings.enforce_strictness && (!self.input.is_empty() || self.depth != 1) {
204 return Err(SerdeError::invalid_input("expected object"));
205 }
206 self.depth -= 1;
208 return Ok(());
209 }
210 if self.remaining().first() != Some(&b'{') {
211 return Err(SerdeError::type_mismatch("expected object"));
212 }
213 self.advance_by(1);
214
215 let mut first = true;
216 loop {
217 if self.next_element(first, b'}', "object")? {
219 break;
220 }
221 first = false;
222 if self.remaining().first() != Some(&b'"') {
223 return Err(SerdeError::invalid_input("expected object key"));
224 }
225
226 let key_str = self.parse_key()?;
228
229 self.skip_whitespace();
231 if self.remaining().first() != Some(&b':') {
232 return Err(SerdeError::invalid_input("expected colon after key"));
233 }
234 self.advance_by(1);
235 self.skip_whitespace();
236
237 let member_schema = self.resolve_member(schema, &key_str);
240 let rem = self.remaining();
241 if !self.settings.dispatch_null_members()
242 && rem.starts_with(b"null")
243 && !rem.get(4).is_some_and(|b| b.is_ascii_alphanumeric())
244 && !member_schema.is_some_and(|member| member.shape_type() == ShapeType::Document)
245 {
246 self.advance_by(4);
247 } else if let Some(member_schema) = member_schema {
248 consumer(member_schema, self)?;
249 } else if &*key_str == "__type" {
250 self.skip_json_value()?;
252 } else {
253 let start = self.position;
256 consumer(&aws_smithy_schema::prelude::DOCUMENT, self)?;
257 if self.position == start {
258 self.skip_json_value()?;
259 }
260 }
261 }
262
263 self.leave_container()?;
264 Ok(())
265 }
266
267 fn read_list(
268 &mut self,
269 _schema: &Schema<'_>,
270 consumer: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
271 ) -> Result<(), SerdeError> {
272 self.depth += 1;
273 if self.depth > self.settings.max_depth() {
274 return Err(SerdeError::custom("maximum nesting depth exceeded"));
275 }
276 self.skip_whitespace();
277 if self.remaining().first() != Some(&b'[') {
278 return Err(SerdeError::type_mismatch("expected array"));
279 }
280 self.advance_by(1);
281
282 let mut first = true;
283 loop {
284 if self.next_element(first, b']', "array")? {
285 break;
286 }
287 first = false;
288 consumer(self)?;
289 }
290
291 self.leave_container()?;
292 Ok(())
293 }
294
295 fn read_map(
296 &mut self,
297 _schema: &Schema<'_>,
298 consumer: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
299 ) -> Result<(), SerdeError> {
300 self.depth += 1;
301 if self.depth > self.settings.max_depth() {
302 return Err(SerdeError::custom("maximum nesting depth exceeded"));
303 }
304 self.skip_whitespace();
305 if self.remaining().first() != Some(&b'{') {
306 return Err(SerdeError::type_mismatch("expected object"));
307 }
308 self.advance_by(1);
309
310 let mut first = true;
311 loop {
312 if self.next_element(first, b'}', "object")? {
313 break;
314 }
315 first = false;
316 if self.remaining().first() != Some(&b'"') {
317 return Err(SerdeError::invalid_input("expected key"));
318 }
319
320 let key = self.parse_key()?;
321
322 self.skip_whitespace();
323 if self.remaining().first() != Some(&b':') {
324 return Err(SerdeError::invalid_input("expected colon"));
325 }
326 self.advance_by(1);
327 self.skip_whitespace();
328
329 consumer(key.into_owned(), self)?;
330 }
331
332 self.leave_container()?;
333 Ok(())
334 }
335
336 fn read_boolean(&mut self, _schema: &Schema<'_>) -> Result<bool, SerdeError> {
337 self.skip_whitespace();
338 let rem = self.remaining();
339 if rem.starts_with(b"true") {
340 self.advance_by(4);
341 Ok(true)
342 } else if rem.starts_with(b"false") {
343 self.advance_by(5);
344 Ok(false)
345 } else {
346 Err(SerdeError::type_mismatch("expected boolean"))
347 }
348 }
349
350 fn read_byte(&mut self, _schema: &Schema<'_>) -> Result<i8, SerdeError> {
351 self.read_integer_value().and_then(|n| {
352 i8::try_from(n).map_err(|_| SerdeError::invalid_input("value out of range for byte"))
353 })
354 }
355
356 fn read_short(&mut self, _schema: &Schema<'_>) -> Result<i16, SerdeError> {
357 self.read_integer_value().and_then(|n| {
358 i16::try_from(n).map_err(|_| SerdeError::invalid_input("value out of range for short"))
359 })
360 }
361
362 fn read_integer(&mut self, _schema: &Schema<'_>) -> Result<i32, SerdeError> {
363 self.read_integer_value().and_then(|n| {
364 i32::try_from(n)
365 .map_err(|_| SerdeError::invalid_input("value out of range for integer"))
366 })
367 }
368
369 fn read_long(&mut self, _schema: &Schema<'_>) -> Result<i64, SerdeError> {
370 self.read_integer_value()
371 }
372
373 fn read_float(&mut self, _schema: &Schema<'_>) -> Result<f32, SerdeError> {
374 self.read_float_value().map(|f| f as f32)
375 }
376
377 fn read_double(&mut self, _schema: &Schema<'_>) -> Result<f64, SerdeError> {
378 self.read_float_value()
379 }
380
381 fn read_big_integer(&mut self, _schema: &Schema<'_>) -> Result<BigInteger, SerdeError> {
382 use std::str::FromStr;
383 self.skip_whitespace();
384 match self.remaining().first() {
385 Some(b'-') | Some(b'0'..=b'9') => {
386 let start = self.position;
387 self.consume_number()?;
388 let num_str = std::str::from_utf8(&self.input[start..self.position])
389 .map_err(|e| SerdeError::invalid_input(e.to_string()))?;
390 BigInteger::from_str(num_str).map_err(|e| SerdeError::invalid_input(e.to_string()))
391 }
392 Some(b'"') => {
398 let s = self.read_string(_schema)?;
399 BigInteger::from_str(&s).map_err(|e| SerdeError::invalid_input(e.to_string()))
400 }
401 _ => Err(SerdeError::type_mismatch("expected number or string")),
402 }
403 }
404
405 fn read_big_decimal(&mut self, _schema: &Schema<'_>) -> Result<BigDecimal, SerdeError> {
406 use std::str::FromStr;
407 self.skip_whitespace();
408 match self.remaining().first() {
409 Some(b'-') | Some(b'0'..=b'9') => {
410 let start = self.position;
411 self.consume_number()?;
412 let num_str = std::str::from_utf8(&self.input[start..self.position])
413 .map_err(|e| SerdeError::invalid_input(e.to_string()))?;
414 BigDecimal::from_str(num_str).map_err(|e| SerdeError::invalid_input(e.to_string()))
415 }
416 Some(b'"') => {
419 let s = self.read_string(_schema)?;
420 BigDecimal::from_str(&s).map_err(|e| SerdeError::invalid_input(e.to_string()))
421 }
422 _ => Err(SerdeError::type_mismatch("expected number or string")),
423 }
424 }
425
426 fn read_string(&mut self, _schema: &Schema<'_>) -> Result<String, SerdeError> {
427 self.skip_whitespace();
428 let pos = self.position;
429 let input = self.input;
430 let rem = &input[pos..];
431 if rem.first() != Some(&b'"') {
432 return Err(SerdeError::type_mismatch("expected string"));
433 }
434 let mut i = 1;
436 let mut has_escape = false;
437 while i < rem.len() {
438 if rem[i] == b'\\' {
439 has_escape = true;
440 i += 2;
441 } else if rem[i] == b'"' {
442 let raw = &input[pos + 1..pos + i];
443 self.position = pos + i + 1;
444 if !has_escape {
445 return std::str::from_utf8(raw)
446 .map(|s| s.to_owned())
447 .map_err(|e| SerdeError::invalid_input(e.to_string()));
448 }
449 let s = std::str::from_utf8(raw)
450 .map_err(|e| SerdeError::invalid_input(e.to_string()))?;
451 return crate::deserialize::EscapedStr::new(s)
452 .to_unescaped()
453 .map(|s| s.into_owned())
454 .map_err(|e| SerdeError::invalid_input(e.to_string()));
455 } else {
456 if self.settings.enforce_strictness && rem[i] < 32 {
457 return Err(SerdeError::invalid_input("raw control character in string"));
458 }
459 i += 1;
460 }
461 }
462 Err(SerdeError::invalid_input("unterminated string"))
463 }
464
465 fn read_blob(&mut self, _schema: &Schema<'_>) -> Result<Blob, SerdeError> {
466 let s = self.read_string(_schema)?;
467 let decoded = aws_smithy_types::base64::decode(&s)
468 .map_err(|e| SerdeError::invalid_input(format!("invalid base64: {}", e)))?;
469 Ok(Blob::new(decoded))
470 }
471
472 fn read_string_list(&mut self, _schema: &Schema<'_>) -> Result<Vec<String>, SerdeError> {
473 self.depth += 1;
474 if self.depth > self.settings.max_depth() {
475 return Err(SerdeError::custom("maximum nesting depth exceeded"));
476 }
477 self.skip_whitespace();
478 if self.remaining().first() != Some(&b'[') {
479 return Err(SerdeError::type_mismatch("expected array"));
480 }
481 self.advance_by(1);
482 let mut out = Vec::new();
483 let mut first = true;
484 loop {
485 if self.next_element(first, b']', "array")? {
486 break;
487 }
488 first = false;
489 out.push(self.read_string(_schema)?);
490 }
491 self.leave_container()?;
492 Ok(out)
493 }
494
495 fn read_blob_list(&mut self, _schema: &Schema<'_>) -> Result<Vec<Blob>, SerdeError> {
496 self.depth += 1;
497 if self.depth > self.settings.max_depth() {
498 return Err(SerdeError::custom("maximum nesting depth exceeded"));
499 }
500 self.skip_whitespace();
501 if self.remaining().first() != Some(&b'[') {
502 return Err(SerdeError::type_mismatch("expected array"));
503 }
504 self.advance_by(1);
505 let mut out = Vec::new();
506 let mut first = true;
507 loop {
508 if self.next_element(first, b']', "array")? {
509 break;
510 }
511 first = false;
512 out.push(self.read_blob(_schema)?);
513 }
514 self.leave_container()?;
515 Ok(out)
516 }
517
518 fn read_integer_list(&mut self, _schema: &Schema<'_>) -> Result<Vec<i32>, SerdeError> {
519 self.depth += 1;
520 if self.depth > self.settings.max_depth() {
521 return Err(SerdeError::custom("maximum nesting depth exceeded"));
522 }
523 self.skip_whitespace();
524 if self.remaining().first() != Some(&b'[') {
525 return Err(SerdeError::type_mismatch("expected array"));
526 }
527 self.advance_by(1);
528 let mut out = Vec::new();
529 let mut first = true;
530 loop {
531 if self.next_element(first, b']', "array")? {
532 break;
533 }
534 first = false;
535 out.push(self.read_integer(_schema)?);
536 }
537 self.leave_container()?;
538 Ok(out)
539 }
540
541 fn read_long_list(&mut self, _schema: &Schema<'_>) -> Result<Vec<i64>, SerdeError> {
542 self.depth += 1;
543 if self.depth > self.settings.max_depth() {
544 return Err(SerdeError::custom("maximum nesting depth exceeded"));
545 }
546 self.skip_whitespace();
547 if self.remaining().first() != Some(&b'[') {
548 return Err(SerdeError::type_mismatch("expected array"));
549 }
550 self.advance_by(1);
551 let mut out = Vec::new();
552 let mut first = true;
553 loop {
554 if self.next_element(first, b']', "array")? {
555 break;
556 }
557 first = false;
558 out.push(self.read_long(_schema)?);
559 }
560 self.leave_container()?;
561 Ok(out)
562 }
563
564 fn read_string_string_map(
565 &mut self,
566 _schema: &Schema<'_>,
567 ) -> Result<std::collections::HashMap<String, String>, SerdeError> {
568 self.depth += 1;
569 if self.depth > self.settings.max_depth() {
570 return Err(SerdeError::custom("maximum nesting depth exceeded"));
571 }
572 self.skip_whitespace();
573 if self.remaining().first() != Some(&b'{') {
574 return Err(SerdeError::type_mismatch("expected object"));
575 }
576 self.advance_by(1);
577 let mut out = std::collections::HashMap::new();
578 let mut first = true;
579 loop {
580 if self.next_element(first, b'}', "object")? {
581 break;
582 }
583 first = false;
584 if self.remaining().first() != Some(&b'"') {
585 return Err(SerdeError::invalid_input("expected key"));
586 }
587 let key = self.parse_key()?;
588 self.skip_whitespace();
589 if self.remaining().first() != Some(&b':') {
590 return Err(SerdeError::invalid_input("expected colon"));
591 }
592 self.advance_by(1);
593 self.skip_whitespace();
594 let val = self.read_string(_schema)?;
595 out.insert(key.into_owned(), val);
596 }
597 self.leave_container()?;
598 Ok(out)
599 }
600
601 fn read_timestamp(&mut self, schema: &Schema<'_>) -> Result<DateTime, SerdeError> {
602 use aws_smithy_schema::traits::TimestampFormat;
603 use aws_smithy_types::date_time::Format;
604
605 self.skip_whitespace();
606 let strict = self.settings.strict_timestamp_formats();
607 let format = match schema.timestamp_format().map(|t| t.format()) {
609 Some(TimestampFormat::EpochSeconds) => Format::EpochSeconds,
610 Some(TimestampFormat::DateTime) => Format::DateTime,
611 Some(TimestampFormat::HttpDate) => Format::HttpDate,
612 Some(other) => {
613 return Err(SerdeError::unsupported(format!(
614 "unsupported timestamp format {other:?}"
615 )))
616 }
617 None => self.settings.default_timestamp_format(),
618 };
619 match self.remaining().first() {
620 Some(b'"') => {
621 let string_format = if strict {
622 if matches!(format, Format::EpochSeconds) {
626 return Err(SerdeError::type_mismatch(
627 "expected a JSON number for an epoch-seconds timestamp",
628 ));
629 }
630 format
631 } else {
632 match schema.timestamp_format().map(|t| t.format()) {
637 Some(TimestampFormat::HttpDate) => Format::HttpDate,
638 Some(TimestampFormat::EpochSeconds) => Format::EpochSeconds,
639 Some(TimestampFormat::DateTime) => Format::DateTimeWithOffset,
640 Some(other) => {
641 return Err(SerdeError::unsupported(format!(
642 "unsupported timestamp format {other:?}"
643 )))
644 }
645 None => crate::codec::string_timestamp_format(
646 self.settings.default_timestamp_format(),
647 ),
648 }
649 };
650 let s = self.read_string(schema)?;
651 DateTime::from_str(&s, string_format)
652 .map_err(|e| SerdeError::custom(format!("invalid timestamp string: {e}")))
653 }
654 Some(b'-') | Some(b'0'..=b'9') => {
655 if strict && !matches!(format, Format::EpochSeconds) {
656 return Err(SerdeError::type_mismatch(
657 "expected a JSON string for a date-time or http-date timestamp",
658 ));
659 }
660 let start = self.position;
662 self.consume_number()?;
663 let num_str = std::str::from_utf8(&self.input[start..self.position])
664 .map_err(|e| SerdeError::invalid_input(e.to_string()))?;
665 if num_str.contains('.') || num_str.contains('e') || num_str.contains('E') {
666 let f: f64 = num_str.parse().map_err(|e: std::num::ParseFloatError| {
667 SerdeError::invalid_input(e.to_string())
668 })?;
669 const I64_MIN_F64: f64 = i64::MIN as f64;
673 const I64_MAX_F64: f64 = i64::MAX as f64;
674 if self.settings.enforce_strictness
675 && (!f.is_finite() || !(I64_MIN_F64..I64_MAX_F64).contains(&f))
676 {
677 return Err(SerdeError::invalid_input(
678 "epoch-seconds value out of range",
679 ));
680 }
681 Ok(DateTime::from_secs_f64(f))
682 } else if num_str.starts_with('-') {
683 let n: i64 = num_str.parse().map_err(|e: std::num::ParseIntError| {
684 SerdeError::invalid_input(e.to_string())
685 })?;
686 Ok(DateTime::from_secs(n))
687 } else {
688 let n: u64 = num_str.parse().map_err(|e: std::num::ParseIntError| {
689 SerdeError::invalid_input(e.to_string())
690 })?;
691 if n > i64::MAX as u64 {
692 return Err(SerdeError::invalid_input(format!(
693 "epoch-seconds value {n} overflows i64; cannot construct DateTime"
694 )));
695 }
696 Ok(DateTime::from_secs(n as i64))
697 }
698 }
699 _ => Err(SerdeError::type_mismatch("expected timestamp")),
700 }
701 }
702
703 fn read_document(&mut self, _schema: &Schema<'_>) -> Result<Document, SerdeError> {
704 self.depth += 1;
705 if self.depth > self.settings.max_depth() {
706 return Err(SerdeError::custom("maximum nesting depth exceeded"));
707 }
708 self.skip_whitespace();
709 let result: Result<Document, SerdeError> = match self.remaining().first() {
710 Some(b'"') => Ok(Document::String(self.read_string(_schema)?)),
711 Some(b't') | Some(b'f') => Ok(Document::Bool(self.read_boolean(_schema)?)),
712 Some(b'n') => {
713 if self.remaining().starts_with(b"null") {
714 self.advance_by(4);
715 Ok(Document::Null)
716 } else {
717 Err(SerdeError::invalid_input("unexpected token in document"))
718 }
719 }
720 Some(b'{') => {
721 self.advance_by(1);
722 let mut map = std::collections::HashMap::new();
723 let mut first = true;
724 loop {
725 if self.next_element(first, b'}', "document object")? {
726 break;
727 }
728 first = false;
729 if self.remaining().first() != Some(&b'"') {
730 return Err(SerdeError::invalid_input("expected object key in document"));
731 }
732 let key = self.parse_key()?.into_owned();
733 self.skip_whitespace();
734 if self.remaining().first() != Some(&b':') {
735 return Err(SerdeError::invalid_input(
736 "expected colon in document object",
737 ));
738 }
739 self.advance_by(1);
740 self.skip_whitespace();
741 let value = self.read_document(_schema)?;
742 map.insert(key, value);
743 }
744 Ok(Document::Object(map))
745 }
746 Some(b'[') => {
747 self.advance_by(1);
748 let mut arr = Vec::new();
749 let mut first = true;
750 loop {
751 if self.next_element(first, b']', "document array")? {
752 break;
753 }
754 first = false;
755 arr.push(self.read_document(_schema)?);
756 }
757 Ok(Document::Array(arr))
758 }
759 Some(c) if *c == b'-' || c.is_ascii_digit() => {
760 let pos = self.position;
768 self.consume_number()?;
769 let token = &self.input[pos..self.position];
770 let is_negative = token.first() == Some(&b'-');
771 let is_float = token.iter().any(|b| matches!(b, b'.' | b'e' | b'E'));
772 let s = std::str::from_utf8(token)
773 .map_err(|e| SerdeError::invalid_input(e.to_string()))?;
774 if is_float {
791 match s.parse::<f64>() {
792 Ok(f) => Ok(Document::Number(Number::Float(f))),
793 Err(e) => Err(SerdeError::invalid_input(e.to_string())),
794 }
795 } else if is_negative {
796 match s.parse::<i64>() {
797 Ok(n) => Ok(Document::Number(Number::NegInt(n))),
798 Err(_) => match s.parse::<f64>() {
800 Ok(f) => Ok(Document::Number(Number::Float(f))),
801 Err(e) => Err(SerdeError::invalid_input(e.to_string())),
802 },
803 }
804 } else {
805 match s.parse::<u64>() {
806 Ok(n) => Ok(Document::Number(Number::PosInt(n))),
807 Err(_) => match s.parse::<f64>() {
809 Ok(f) => Ok(Document::Number(Number::Float(f))),
810 Err(e) => Err(SerdeError::invalid_input(e.to_string())),
811 },
812 }
813 }
814 }
815 _ => Err(SerdeError::invalid_input("unexpected token in document")),
816 };
817 if result.is_ok() {
818 self.leave_container()?;
819 }
820 result
821 }
822
823 fn is_null(&self) -> bool {
824 let remaining = self.remaining();
825 remaining.len() >= 4
826 && &remaining[..4] == b"null"
827 && !remaining.get(4).is_some_and(|b| b.is_ascii_alphanumeric())
828 }
829
830 fn read_null(&mut self) -> Result<(), SerdeError> {
831 self.skip_whitespace();
832 if self.is_null() {
833 self.advance_by(4);
834 }
835 Ok(())
836 }
837
838 fn skip_value(&mut self) -> Result<(), SerdeError> {
843 self.skip_json_value()
844 }
845
846 fn container_size(&self) -> Option<usize> {
847 let mut iter = json_token_iter(self.remaining());
848 match iter.next()? {
849 Ok(Token::StartArray { .. }) => {
850 let mut count = 0;
851 let mut depth = 1;
852 for token in iter {
853 match token {
854 Ok(Token::StartArray { .. }) | Ok(Token::StartObject { .. }) => {
855 if depth == 1 {
856 count += 1;
857 }
858 depth += 1;
859 }
860 Ok(Token::EndArray { .. }) | Ok(Token::EndObject { .. }) => {
861 depth -= 1;
862 if depth == 0 {
863 return Some(count);
864 }
865 }
866 Ok(Token::ValueBool { .. })
867 | Ok(Token::ValueNull { .. })
868 | Ok(Token::ValueString { .. })
869 | Ok(Token::ValueNumber { .. })
870 if depth == 1 =>
871 {
872 count += 1
873 }
874 _ => {}
875 }
876 }
877 None
878 }
879 Ok(Token::StartObject { .. }) => {
880 let mut count = 0;
881 let mut depth = 1;
882 for token in iter {
883 match token {
884 Ok(Token::StartArray { .. }) | Ok(Token::StartObject { .. }) => depth += 1,
885 Ok(Token::EndArray { .. }) | Ok(Token::EndObject { .. }) => {
886 depth -= 1;
887 if depth == 0 {
888 return Some(count);
889 }
890 }
891 Ok(Token::ObjectKey { .. }) if depth == 1 => count += 1,
892 _ => {}
893 }
894 }
895 None
896 }
897 _ => None,
898 }
899 }
900}
901
902impl<'a> JsonDeserializer<'a> {
903 pub fn read_discriminated_document(&mut self) -> Result<DiscriminatedDocument, SerdeError> {
946 let dummy_schema = &aws_smithy_schema::prelude::DOCUMENT;
952 let mut doc = self.read_document(dummy_schema)?;
953 let mut discriminator: Option<String> = None;
954 if let Document::Object(ref mut map) = doc {
955 let lift_candidate = match map.get("__type") {
960 Some(Document::String(s)) => {
961 Self::resolve_shape_id(s, self.settings.default_namespace())
962 }
963 _ => None,
964 };
965 if let Some(id) = lift_candidate {
966 map.remove("__type");
967 discriminator = Some(id);
968 }
969 }
970
971 let settings: Arc<dyn DocumentSettings> = self.settings.clone();
972 let mut wrapper = DiscriminatedDocument::new(doc).with_settings(settings);
973 if let Some(id) = discriminator {
974 wrapper = wrapper.with_discriminator(id);
975 }
976 Ok(wrapper)
977 }
978
979 fn skip_whitespace(&mut self) {
980 while self.position < self.input.len() {
981 match self.input[self.position] {
982 b' ' | b'\t' | b'\n' | b'\r' => self.position += 1,
983 _ => break,
984 }
985 }
986 }
987
988 fn next_element(&mut self, first: bool, close: u8, what: &str) -> Result<bool, SerdeError> {
996 self.skip_whitespace();
997 match self.remaining().first().copied() {
998 Some(b) if b == close => {
999 self.advance_by(1);
1000 Ok(true)
1001 }
1002 None => Err(SerdeError::invalid_input(format!(
1003 "unexpected end of input in {what}"
1004 ))),
1005 Some(b',') if first => Err(SerdeError::invalid_input(format!(
1006 "unexpected `,` before the first element of {what}"
1007 ))),
1008 Some(b',') => {
1009 self.advance_by(1);
1010 self.skip_whitespace();
1011 match self.remaining().first().copied() {
1012 Some(b) if b == close => {
1013 Err(SerdeError::invalid_input(format!("trailing `,` in {what}")))
1014 }
1015 Some(b',') => Err(SerdeError::invalid_input(format!("repeated `,` in {what}"))),
1016 None => Err(SerdeError::invalid_input(format!(
1017 "unexpected end of input in {what}"
1018 ))),
1019 Some(_) => Ok(false),
1020 }
1021 }
1022 Some(_) if first => Ok(false),
1023 Some(_) => Err(SerdeError::invalid_input(format!(
1024 "expected `,` between elements of {what}"
1025 ))),
1026 }
1027 }
1028
1029 fn enter_container(&mut self) -> Result<(), SerdeError> {
1030 self.depth += 1;
1031 if self.depth > self.settings.max_depth() {
1032 return Err(SerdeError::custom("maximum nesting depth exceeded"));
1033 }
1034 Ok(())
1035 }
1036
1037 fn leave_container(&mut self) -> Result<(), SerdeError> {
1042 self.depth -= 1;
1043 if self.depth == 0 {
1044 self.skip_whitespace();
1045 if !self.remaining().is_empty() {
1046 return Err(SerdeError::invalid_input(
1047 "unexpected trailing characters after the JSON value",
1048 ));
1049 }
1050 }
1051 Ok(())
1052 }
1053
1054 fn skip_number(&mut self) -> Result<(), SerdeError> {
1060 let rem = self.remaining();
1061 let invalid = || SerdeError::invalid_input("invalid number");
1062 let mut i = 0;
1063 if rem.get(i) == Some(&b'-') {
1064 i += 1;
1065 }
1066 match rem.get(i) {
1067 Some(b'0') if !self.settings.allow_leading_zeros => i += 1,
1068 Some(b'0') => {
1069 while rem.get(i).is_some_and(|b| b.is_ascii_digit()) {
1070 i += 1;
1071 }
1072 }
1073 Some(b'1'..=b'9') => {
1074 while rem.get(i).is_some_and(|b| b.is_ascii_digit()) {
1075 i += 1;
1076 }
1077 }
1078 Some(b'.')
1079 if self.settings.allow_empty_integer_part
1080 && i == 1
1081 && rem.first() == Some(&b'-')
1082 && rem.get(i + 1).is_some_and(|b| b.is_ascii_digit()) => {}
1083 _ => return Err(invalid()),
1084 }
1085 if rem.get(i) == Some(&b'.') {
1086 i += 1;
1087 let start = i;
1088 while rem.get(i).is_some_and(|b| b.is_ascii_digit()) {
1089 i += 1;
1090 }
1091 if i == start && !self.settings.allow_trailing_decimal_point {
1092 return Err(invalid());
1093 }
1094 }
1095 if matches!(rem.get(i), Some(b'e') | Some(b'E')) {
1096 i += 1;
1097 if matches!(rem.get(i), Some(b'+') | Some(b'-')) {
1098 i += 1;
1099 }
1100 let start = i;
1101 while rem.get(i).is_some_and(|b| b.is_ascii_digit()) {
1102 i += 1;
1103 }
1104 if i == start {
1105 return Err(invalid());
1106 }
1107 }
1108 self.advance_by(i);
1109 Ok(())
1110 }
1111
1112 fn skip_string(&mut self) -> Result<(), SerdeError> {
1114 let rem = self.remaining();
1115 debug_assert_eq!(rem.first(), Some(&b'"'));
1116 let mut i = 1;
1117 loop {
1118 match rem.get(i) {
1119 None => return Err(SerdeError::invalid_input("unterminated string")),
1120 Some(b'"') => {
1121 i += 1;
1122 break;
1123 }
1124 Some(b'\\') if !self.settings.validate_skipped_values => i += 2,
1125 Some(b'\\') => match rem.get(i + 1) {
1126 Some(b'"' | b'\\' | b'/' | b'b' | b'f' | b'n' | b'r' | b't') => i += 2,
1127 Some(b'u')
1128 if rem
1129 .get(i + 2..i + 6)
1130 .is_some_and(|hex| hex.iter().all(u8::is_ascii_hexdigit)) =>
1131 {
1132 i += 6
1133 }
1134 _ => {
1135 return Err(SerdeError::invalid_input(
1136 "invalid escape sequence in string",
1137 ))
1138 }
1139 },
1140 Some(byte) if self.settings.validate_skipped_string_encoding && *byte < 0x20 => {
1141 return Err(SerdeError::invalid_input(
1142 "raw control character in skipped string",
1143 ));
1144 }
1145 Some(_) => i += 1,
1146 }
1147 }
1148 if self.settings.validate_skipped_string_encoding {
1149 std::str::from_utf8(&rem[..i])
1150 .map_err(|_| SerdeError::invalid_input("invalid UTF-8 in skipped string"))?;
1151 }
1152 self.advance_by(i);
1153 Ok(())
1154 }
1155
1156 fn skip_literal(&mut self, literal: &'static [u8]) -> Result<(), SerdeError> {
1157 if !self.remaining().starts_with(literal) {
1158 return Err(SerdeError::invalid_input(format!(
1159 "expected `{}`",
1160 String::from_utf8_lossy(literal)
1161 )));
1162 }
1163 self.advance_by(literal.len());
1164 Ok(())
1165 }
1166
1167 fn consume_number(&mut self) -> Result<(), SerdeError> {
1168 if self.settings.enforce_strictness {
1169 self.skip_number()?;
1170 if self
1172 .remaining()
1173 .first()
1174 .is_some_and(|b| !matches!(b, b' ' | b'\t' | b'\r' | b'\n' | b',' | b']' | b'}'))
1175 {
1176 return Err(SerdeError::invalid_input("invalid number boundary"));
1177 }
1178 return Ok(());
1179 }
1180 let mut len = 0;
1181 for &b in self.remaining() {
1182 if b.is_ascii_digit() || b == b'-' || b == b'.' || b == b'e' || b == b'E' || b == b'+' {
1183 len += 1;
1184 } else {
1185 break;
1186 }
1187 }
1188 self.advance_by(len);
1189 Ok(())
1190 }
1191
1192 fn skip_json_value(&mut self) -> Result<(), SerdeError> {
1196 self.skip_whitespace();
1197 match self.remaining().first().copied() {
1198 Some(b'{') => {
1199 self.enter_container()?;
1200 self.advance_by(1);
1201 let mut first = true;
1202 loop {
1203 if self.next_element(first, b'}', "object")? {
1204 break;
1205 }
1206 first = false;
1207 if self.remaining().first() != Some(&b'"') {
1208 return Err(SerdeError::invalid_input("expected object key"));
1209 }
1210 self.skip_string()?;
1211 self.skip_whitespace();
1212 if self.remaining().first() != Some(&b':') {
1213 return Err(SerdeError::invalid_input("expected colon after key"));
1214 }
1215 self.advance_by(1);
1216 self.skip_json_value()?;
1217 }
1218 self.depth -= 1;
1219 Ok(())
1220 }
1221 Some(b'[') => {
1222 self.enter_container()?;
1223 self.advance_by(1);
1224 let mut first = true;
1225 loop {
1226 if self.next_element(first, b']', "array")? {
1227 break;
1228 }
1229 first = false;
1230 self.skip_json_value()?;
1231 }
1232 self.depth -= 1;
1233 Ok(())
1234 }
1235 Some(b'"') => self.skip_string(),
1236 Some(b't') => self.skip_literal(b"true"),
1237 Some(b'f') => self.skip_literal(b"false"),
1238 Some(b'n') => self.skip_literal(b"null"),
1239 Some(c) if c == b'-' || c.is_ascii_digit() => self.skip_number(),
1240 Some(_) => Err(SerdeError::invalid_input("unexpected token in skip_value")),
1241 None => Err(SerdeError::invalid_input("unexpected end of input")),
1242 }
1243 }
1244
1245 fn read_integer_value(&mut self) -> Result<i64, SerdeError> {
1246 self.skip_whitespace();
1247 if self.settings.enforce_strictness || self.settings.allow_integral_float_numbers {
1248 let start = self.position;
1249 self.skip_number()?;
1250 if self
1251 .remaining()
1252 .first()
1253 .is_some_and(|b| !matches!(b, b' ' | b'\t' | b'\r' | b'\n' | b',' | b']' | b'}'))
1254 {
1255 return Err(SerdeError::invalid_input("invalid number boundary"));
1256 }
1257 let text = std::str::from_utf8(&self.input[start..self.position])
1258 .map_err(|e| SerdeError::invalid_input(e.to_string()))?;
1259 if self.settings.allow_integral_float_numbers && text.contains(['.', 'e', 'E']) {
1260 return parse_integral_decimal(text);
1261 }
1262 return text
1263 .parse()
1264 .map_err(|e: std::num::ParseIntError| SerdeError::invalid_input(e.to_string()));
1265 }
1266 let rem = self.remaining();
1267 let mut len = 0;
1268 for &b in rem {
1269 if b.is_ascii_digit() || b == b'-' || b == b'+' {
1270 len += 1;
1271 } else {
1272 break;
1273 }
1274 }
1275 if len == 0 {
1276 return Err(SerdeError::type_mismatch("expected integer"));
1277 }
1278 if let Some(&next) = rem.get(len) {
1282 if next == b'.' || next == b'e' || next == b'E' {
1283 return Err(SerdeError::type_mismatch(
1284 "expected integer, found floating-point number",
1285 ));
1286 }
1287 }
1288 let s = std::str::from_utf8(&rem[..len])
1289 .map_err(|e| SerdeError::invalid_input(e.to_string()))?;
1290 let n = s
1291 .parse::<i64>()
1292 .map_err(|e| SerdeError::invalid_input(e.to_string()))?;
1293 self.advance_by(len);
1294 Ok(n)
1295 }
1296
1297 fn read_float_value(&mut self) -> Result<f64, SerdeError> {
1298 self.skip_whitespace();
1299 let rem = self.remaining();
1300 if rem.first() == Some(&b'"') {
1310 let s = self.read_string(&aws_smithy_schema::prelude::STRING)?;
1311 let value = match s.as_str() {
1312 "NaN" => f64::NAN,
1313 "Infinity" => f64::INFINITY,
1314 "-Infinity" => f64::NEG_INFINITY,
1315 other => other
1316 .parse::<f64>()
1317 .map_err(|e| SerdeError::invalid_input(e.to_string()))?,
1318 };
1319 if value.is_finite() {
1320 return Err(SerdeError::type_mismatch(format!(
1321 "only `Infinity`, `-Infinity`, `NaN` can represent a float as a string but found `{s}`"
1322 )));
1323 }
1324 return Ok(value);
1325 }
1326 if self.settings.enforce_strictness {
1327 let start = self.position;
1328 self.consume_number()?;
1329 return std::str::from_utf8(&self.input[start..self.position])
1330 .map_err(|e| SerdeError::invalid_input(e.to_string()))?
1331 .parse()
1332 .map_err(|e: std::num::ParseFloatError| SerdeError::invalid_input(e.to_string()));
1333 }
1334 let mut len = 0;
1335 for &b in rem {
1336 if b.is_ascii_digit() || b == b'-' || b == b'+' || b == b'.' || b == b'e' || b == b'E' {
1337 len += 1;
1338 } else {
1339 break;
1340 }
1341 }
1342 if len == 0 {
1343 return Err(SerdeError::type_mismatch("expected number"));
1344 }
1345 let s = std::str::from_utf8(&rem[..len])
1346 .map_err(|e| SerdeError::invalid_input(e.to_string()))?;
1347 let n = s
1348 .parse::<f64>()
1349 .map_err(|e| SerdeError::invalid_input(e.to_string()))?;
1350 self.advance_by(len);
1351 Ok(n)
1352 }
1353}
1354
1355fn parse_integral_decimal(text: &str) -> Result<i64, SerdeError> {
1374 let invalid = || SerdeError::invalid_input("number is fractional or outside integer range");
1375 let negative = text.starts_with('-');
1376 let unsigned = text.strip_prefix('-').unwrap_or(text);
1377 let (mantissa, exponent) = unsigned.split_once(['e', 'E']).unwrap_or((unsigned, "0"));
1378 let (int_part, frac_part) = mantissa.split_once('.').unwrap_or((mantissa, ""));
1379 let exponent: i64 = exponent.parse().map_err(|_| invalid())?;
1380
1381 let all_digits = int_part.bytes().chain(frac_part.bytes());
1382 let leading_zeros = all_digits.clone().take_while(|b| *b == b'0').count();
1383 let digits = all_digits.skip(leading_zeros).map(|b| (b - b'0') as i128);
1384 let significant = (int_part.len() + frac_part.len() - leading_zeros) as i64;
1385 if significant == 0 {
1386 return Ok(0);
1387 }
1388 let shift = exponent
1389 .checked_sub(frac_part.len() as i64)
1390 .ok_or_else(invalid)?;
1391
1392 let kept = significant.checked_add(shift).ok_or_else(invalid)?;
1395 if !(1..=19).contains(&kept) {
1396 return Err(invalid());
1397 }
1398 let take = kept.min(significant) as usize;
1399 let mut acc: i128 = 0;
1400 for (i, d) in digits.enumerate() {
1401 if i < take {
1402 acc = acc * 10 + d; } else if d != 0 {
1404 return Err(invalid()); }
1406 }
1407 if shift > 0 {
1408 acc *= 10_i128.pow(shift as u32); }
1410 i64::try_from(if negative { -acc } else { acc }).map_err(|_| invalid())
1411}
1412
1413#[cfg(test)]
1414mod tests {
1415 use super::*;
1416
1417 #[test]
1418 fn null_dispatch_can_clear_an_optional_duplicate_without_changing_the_default() {
1419 use aws_smithy_schema::shape_id;
1420
1421 let text = Schema::new_member(
1422 shape_id!("test", "Input", "text"),
1423 ShapeType::String,
1424 "text",
1425 0,
1426 );
1427 let members = [&text];
1428 let schema = Schema::new_struct(shape_id!("test", "Input"), ShapeType::Structure, &members);
1429 for (dispatch, expected) in [(false, Some("first".to_owned())), (true, None)] {
1430 let settings = JsonCodecSettings::builder()
1431 .dispatch_null_members(dispatch)
1432 .build();
1433 let mut deserializer =
1434 JsonDeserializer::new(br#"{"text":"first","text":null}"#, Arc::new(settings));
1435 let mut value = None;
1436 deserializer
1437 .read_struct(&schema, &mut |member, reader| {
1438 if reader.is_null() {
1439 reader.read_null()?;
1440 value = None;
1441 } else {
1442 value = Some(reader.read_string(member)?);
1443 }
1444 Ok(())
1445 })
1446 .unwrap();
1447 assert_eq!(value, expected);
1448 }
1449 }
1450
1451 #[test]
1452 fn document_member_null_is_dispatched_as_a_value() {
1453 use aws_smithy_schema::shape_id;
1454
1455 let document = Schema::new_member(
1456 shape_id!("test", "Input", "document"),
1457 ShapeType::Document,
1458 "document",
1459 0,
1460 );
1461 let string = Schema::new_member(
1462 shape_id!("test", "Input", "string"),
1463 ShapeType::String,
1464 "string",
1465 1,
1466 );
1467 let members = [&document, &string];
1468 let schema = Schema::new_struct(shape_id!("test", "Input"), ShapeType::Structure, &members);
1469 for (body, expected) in [
1470 (
1471 r#"{"document":null,"string":null,"unknown":null}"#,
1472 Some(Document::Null),
1473 ),
1474 (r#"{"string":null,"unknown":null}"#, None),
1475 (r#"{"document":true}"#, Some(Document::Bool(true))),
1476 ] {
1477 let mut actual = None;
1478 let mut deserializer =
1479 JsonDeserializer::new(body.as_bytes(), Arc::new(JsonCodecSettings::default()));
1480 deserializer
1481 .read_struct(&schema, &mut |member, value| {
1482 assert_eq!(member.member_index(), Some(0), "{body}");
1483 actual = Some(value.read_document(member)?);
1484 Ok(())
1485 })
1486 .unwrap();
1487 assert_eq!(actual, expected, "{body}");
1488 }
1489 }
1490
1491 #[test]
1492 fn empty_integer_part_allowance_matches_legacy_negative_fractions() {
1493 for allow in [false, true] {
1494 for trailing in [false, true] {
1495 let settings = Arc::new(
1496 JsonCodecSettings::builder()
1497 .enforce_strictness(true)
1498 .allow_empty_integer_part(allow)
1499 .allow_trailing_decimal_point(trailing)
1500 .build(),
1501 );
1502 for (text, expected) in [
1503 ("-.123", Some(-0.123)),
1504 ("-.123e2", Some(-12.3)),
1505 ("-.0", Some(-0.0)),
1506 ("-0.123", Some(-0.123)),
1507 (".123", None),
1508 ("-.", None),
1509 ("-.e2", None),
1510 ("-.123e", None),
1511 ("-.123x", None),
1512 ] {
1513 let accepted = expected.is_some() && (allow || text == "-0.123");
1514 let result = JsonDeserializer::new(text.as_bytes(), settings.clone())
1515 .read_double(dummy_schema());
1516 assert_eq!(
1517 result.is_ok(),
1518 accepted,
1519 "{text}, allow={allow}, trailing={trailing}"
1520 );
1521 if accepted {
1522 assert_eq!(result.unwrap(), expected.unwrap());
1523 }
1524 let body = format!(r#"{{"ignored":{text}}}"#);
1525 assert_eq!(
1526 JsonDeserializer::new(body.as_bytes(), settings.clone())
1527 .read_struct(dummy_schema(), &mut |_, _| Ok(()))
1528 .is_ok(),
1529 accepted,
1530 "skipped {text}, allow={allow}, trailing={trailing}",
1531 );
1532 }
1533 }
1534 }
1535 assert!(!JsonCodecSettings::default().allow_empty_integer_part);
1536 assert!(
1537 !JsonCodecSettings::builder()
1538 .build()
1539 .allow_empty_integer_part
1540 );
1541 }
1542
1543 #[test]
1544 fn number_allowances_preserve_other_strict_checks() {
1545 for leading in [false, true] {
1546 for trailing in [false, true] {
1547 let settings = Arc::new(
1548 JsonCodecSettings::builder()
1549 .enforce_strictness(true)
1550 .allow_integral_float_numbers(true)
1551 .allow_leading_zeros(leading)
1552 .allow_trailing_decimal_point(trailing)
1553 .build(),
1554 );
1555 for (text, value, accepted) in [
1556 ("214748364.", 214748364, trailing),
1557 ("0147483648", 147483648, leading),
1558 ("0214748364\r", 214748364, leading),
1559 ("012.e2", 1200, leading && trailing),
1560 ("12e2", 1200, true),
1561 ] {
1562 let result = JsonDeserializer::new(text.as_bytes(), settings.clone())
1563 .read_integer(dummy_schema());
1564 assert_eq!(
1565 result.is_ok(),
1566 accepted,
1567 "{text} leading={leading} trailing={trailing}"
1568 );
1569 if accepted {
1570 assert_eq!(result.unwrap(), value);
1571 }
1572 let body = format!(r#"{{"ignored":{text}}}"#);
1573 assert_eq!(
1574 JsonDeserializer::new(body.as_bytes(), settings.clone())
1575 .read_struct(dummy_schema(), &mut |_, _| Ok(()))
1576 .is_ok(),
1577 accepted
1578 );
1579 assert_eq!(
1580 JsonDeserializer::new(text.as_bytes(), settings.clone())
1581 .read_double(dummy_schema())
1582 .is_ok(),
1583 accepted
1584 );
1585 assert_eq!(
1586 JsonDeserializer::new(text.as_bytes(), settings.clone())
1587 .read_document(dummy_schema())
1588 .is_ok(),
1589 accepted
1590 );
1591 }
1592 for text in [
1593 "1e",
1594 "1e+",
1595 "1..0",
1596 "1e2e3",
1597 "--1",
1598 "+1",
1599 "2147483648",
1600 "1.5",
1601 ] {
1602 assert!(
1603 JsonDeserializer::new(text.as_bytes(), settings.clone())
1604 .read_integer(dummy_schema())
1605 .is_err(),
1606 "{text}"
1607 );
1608 }
1609 assert!(JsonDeserializer::new(b"\"raw\ncontrol\"", settings.clone())
1610 .read_string(dummy_schema())
1611 .is_err());
1612 assert!(JsonDeserializer::new(b"{} garbage", settings)
1613 .read_struct(dummy_schema(), &mut |_, _| Ok(()))
1614 .is_err());
1615 }
1616 }
1617 }
1618
1619 #[test]
1620 fn strict_strings_and_lenient_defaults() {
1621 for input in [b"\"raw\ncontrol\"".as_slice(), b"\"raw\x00control\""] {
1622 let mut lenient = JsonDeserializer::new(input, Arc::new(JsonCodecSettings::default()));
1623 assert!(lenient.read_string(dummy_schema()).is_ok());
1624 for key in [false, true] {
1625 let mut strict = JsonDeserializer::new(
1626 input,
1627 Arc::new(
1628 JsonCodecSettings::builder()
1629 .enforce_strictness(true)
1630 .build(),
1631 ),
1632 );
1633 assert!(if key {
1634 strict.parse_key().map(|_| ())
1635 } else {
1636 strict.read_string(dummy_schema()).map(|_| ())
1637 }
1638 .is_err());
1639 let mut strict = JsonDeserializer::new(
1640 input,
1641 Arc::new(
1642 JsonCodecSettings::builder()
1643 .enforce_strictness(true)
1644 .build(),
1645 ),
1646 );
1647 assert!(strict.skip_string().is_ok());
1648 }
1649 }
1650 for input in [b"\"ok\\n\\u0041\"".as_slice(), "\"hello 世界\"".as_bytes()] {
1651 let mut strict = JsonDeserializer::new(
1652 input,
1653 Arc::new(
1654 JsonCodecSettings::builder()
1655 .enforce_strictness(true)
1656 .build(),
1657 ),
1658 );
1659 assert!(strict.skip_string().is_ok());
1660 }
1661 for strict in [false, true] {
1662 let settings = Arc::new(
1663 JsonCodecSettings::builder()
1664 .enforce_strictness(strict)
1665 .build(),
1666 );
1667 for input in [b"\"\xff\"".as_slice(), b"\"\xc0\x80\""] {
1668 assert!(JsonDeserializer::new(input, settings.clone())
1669 .read_string(dummy_schema())
1670 .is_err());
1671 assert!(JsonDeserializer::new(input, settings.clone())
1672 .parse_key()
1673 .is_err());
1674 }
1675 }
1676 let bad = b"\"\xff\"";
1677 assert!(
1678 JsonDeserializer::new(bad, Arc::new(JsonCodecSettings::default()))
1679 .skip_string()
1680 .is_ok()
1681 );
1682 assert!(JsonDeserializer::new(
1683 bad,
1684 Arc::new(
1685 JsonCodecSettings::builder()
1686 .enforce_strictness(true)
1687 .build()
1688 )
1689 )
1690 .skip_string()
1691 .is_ok());
1692 }
1693
1694 #[test]
1695 fn parse_integral_decimal_exact_cases() {
1696 for (input, expected) in [
1697 ("1.0", Some(1)),
1698 ("1e3", Some(1000)),
1699 ("12.0", Some(12)),
1700 ("100E0", Some(100)),
1701 ("1.50E1", Some(15)),
1702 ("1234567890.000", Some(1234567890)),
1703 ("0.5e1", Some(5)),
1704 ("0.05e2", Some(5)),
1705 ("100e-2", Some(1)),
1706 ("12300e-2", Some(123)),
1707 ("1.23e2", Some(123)),
1708 ("0.0", Some(0)),
1709 ("-0e5", Some(0)),
1710 ("0e-5", Some(0)),
1711 ("0e400", Some(0)),
1712 ("-123456789012345e3", Some(-123456789012345000)),
1713 ("9007199254740993.0", Some(9007199254740993)),
1714 ("9223372036854775807e0", Some(i64::MAX)),
1715 ("-9223372036854775808.0", Some(i64::MIN)),
1716 ("1.5", None),
1717 ("2.5e0", None),
1718 ("123e-2", None),
1719 ("1e-5", None),
1720 ("1e-400", None),
1721 ("1e19", None),
1722 ("1e30", None),
1723 ("9223372036854775808.0", None),
1724 ("-9223372036854775809e0", None),
1725 ("0e999999999999999999999", None),
1726 ] {
1727 assert_eq!(parse_integral_decimal(input).ok(), expected, "{input}");
1728 }
1729 }
1730
1731 #[test]
1732 fn exact_integral_numbers_and_strict_grammar() {
1733 let settings = Arc::new(
1734 JsonCodecSettings::builder()
1735 .enforce_strictness(true)
1736 .allow_integral_float_numbers(true)
1737 .build(),
1738 );
1739 for (input, expected) in [
1740 ("1.0", 1),
1741 ("1e3", 1000),
1742 ("1.20e1", 12),
1743 ("9007199254740993", 9007199254740993),
1744 ("9223372036854775807.0", i64::MAX),
1745 ("-9223372036854775808e0", i64::MIN),
1746 ] {
1747 assert_eq!(
1748 JsonDeserializer::new(input.as_bytes(), settings.clone())
1749 .read_long(dummy_schema())
1750 .unwrap(),
1751 expected,
1752 "{input}"
1753 );
1754 }
1755 for input in [
1756 "0e999999999999999999999",
1757 "1.01",
1758 "1e-3",
1759 "9223372036854775808.0",
1760 "-9223372036854775809.0",
1761 "1e10000",
1762 "12.",
1763 "01",
1764 "+1",
1765 "1e",
1766 "--1",
1767 "1e+",
1768 "1.0.0",
1769 ] {
1770 assert!(
1771 JsonDeserializer::new(input.as_bytes(), settings.clone())
1772 .read_long(dummy_schema())
1773 .is_err(),
1774 "{input}"
1775 );
1776 }
1777 for input in ["12.", "01", "+1", "1e", "1e+", "--1", "1.0.0"] {
1778 assert!(
1779 JsonDeserializer::new(input.as_bytes(), settings.clone())
1780 .read_double(dummy_schema())
1781 .is_err(),
1782 "{input}"
1783 );
1784 }
1785 assert!(matches!(
1788 JsonDeserializer::new(b"1.0", Arc::new(JsonCodecSettings::default()))
1789 .read_long(dummy_schema()),
1790 Err(SerdeError::TypeMismatch { .. })
1791 ));
1792 assert_eq!(
1793 JsonDeserializer::new(b"12.", Arc::new(JsonCodecSettings::default()))
1794 .read_double(dummy_schema())
1795 .unwrap(),
1796 12.0
1797 );
1798 for strict in [false, true] {
1799 let settings = Arc::new(
1800 JsonCodecSettings::builder()
1801 .enforce_strictness(strict)
1802 .allow_integral_float_numbers(true)
1803 .build(),
1804 );
1805 assert_eq!(
1806 JsonDeserializer::new(b"127.0", settings.clone())
1807 .read_byte(dummy_schema())
1808 .unwrap(),
1809 127
1810 );
1811 assert!(JsonDeserializer::new(b"128.0", settings.clone())
1812 .read_byte(dummy_schema())
1813 .is_err());
1814 assert!(JsonDeserializer::new(b"32768e0", settings.clone())
1815 .read_short(dummy_schema())
1816 .is_err());
1817 assert!(JsonDeserializer::new(b"2147483648.0", settings.clone())
1818 .read_integer(dummy_schema())
1819 .is_err());
1820 assert!(JsonDeserializer::new(b"12.", settings)
1821 .read_long(dummy_schema())
1822 .is_err());
1823 }
1824 for input in ["1e999", "-1e999", "9223372036854775808.0"] {
1825 assert!(JsonDeserializer::new(input.as_bytes(), settings.clone())
1826 .read_timestamp(dummy_schema())
1827 .is_err());
1828 }
1829 for (input, valid) in [(b"".as_slice(), true), (b" \n", false), (b"{}", true)] {
1830 assert_eq!(
1831 JsonDeserializer::new(input, settings.clone())
1832 .read_struct(dummy_schema(), &mut |_, _| Ok(()))
1833 .is_ok(),
1834 valid
1835 );
1836 }
1837 }
1838
1839 fn dummy_schema() -> &'static aws_smithy_schema::Schema<'static> {
1840 &aws_smithy_schema::prelude::STRING
1841 }
1842
1843 #[test]
1844 fn test_read_boolean() {
1845 let mut deser = JsonDeserializer::new(b"true", Arc::new(JsonCodecSettings::default()));
1846 assert!(deser.read_boolean(dummy_schema()).unwrap());
1847
1848 let mut deser = JsonDeserializer::new(b"false", Arc::new(JsonCodecSettings::default()));
1849 assert!(!(deser.read_boolean(dummy_schema()).unwrap()));
1850 }
1851
1852 #[test]
1853 fn test_read_integer() {
1854 let mut deser = JsonDeserializer::new(b"42", Arc::new(JsonCodecSettings::default()));
1855 assert_eq!(deser.read_integer(dummy_schema()).unwrap(), 42);
1856
1857 let mut deser = JsonDeserializer::new(b"-123", Arc::new(JsonCodecSettings::default()));
1858 assert_eq!(deser.read_integer(dummy_schema()).unwrap(), -123);
1859 }
1860
1861 #[test]
1862 fn test_read_long() {
1863 let mut deser = JsonDeserializer::new(
1864 b"9223372036854775807",
1865 Arc::new(JsonCodecSettings::default()),
1866 );
1867 assert_eq!(deser.read_long(dummy_schema()).unwrap(), i64::MAX);
1868 }
1869
1870 #[test]
1871 fn test_read_float() {
1872 let mut deser = JsonDeserializer::new(b"3.15", Arc::new(JsonCodecSettings::default()));
1873 assert!((deser.read_float(dummy_schema()).unwrap() - 3.15).abs() < 0.01);
1874 }
1875
1876 #[test]
1877 fn test_read_double() {
1878 let mut deser = JsonDeserializer::new(b"2.72", Arc::new(JsonCodecSettings::default()));
1879 assert!((deser.read_double(dummy_schema()).unwrap() - 2.72).abs() < 0.001);
1880 }
1881
1882 #[test]
1883 fn test_read_string() {
1884 let mut deser =
1885 JsonDeserializer::new(br#""hello world""#, Arc::new(JsonCodecSettings::default()));
1886 assert_eq!(deser.read_string(dummy_schema()).unwrap(), "hello world");
1887
1888 let mut deser =
1889 JsonDeserializer::new(br#""hello\nworld""#, Arc::new(JsonCodecSettings::default()));
1890 assert_eq!(deser.read_string(dummy_schema()).unwrap(), "hello\nworld");
1891 }
1892
1893 #[test]
1894 fn test_is_null() {
1895 let deser = JsonDeserializer::new(b"null", Arc::new(JsonCodecSettings::default()));
1896 assert!(deser.is_null());
1897
1898 let deser = JsonDeserializer::new(b"42", Arc::new(JsonCodecSettings::default()));
1899 assert!(!deser.is_null());
1900 }
1901
1902 #[test]
1903 fn test_read_byte_range() {
1904 let mut deser = JsonDeserializer::new(b"127", Arc::new(JsonCodecSettings::default()));
1905 assert_eq!(deser.read_byte(dummy_schema()).unwrap(), 127);
1906
1907 let mut deser = JsonDeserializer::new(b"128", Arc::new(JsonCodecSettings::default()));
1908 assert!(deser.read_byte(dummy_schema()).is_err());
1909 }
1910
1911 #[test]
1912 fn test_read_struct() {
1913 use aws_smithy_schema::Schema;
1914
1915 #[derive(Debug, Default, PartialEq)]
1916 struct Person {
1917 first_name: String,
1918 last_name: String,
1919 age: i32,
1920 }
1921
1922 static FIRST_NAME: Schema<'static> = Schema::new_member(
1923 aws_smithy_schema::shape_id!("test", "Person"),
1924 aws_smithy_schema::ShapeType::String,
1925 "firstName",
1926 0,
1927 );
1928 static LAST_NAME: Schema<'static> = Schema::new_member(
1929 aws_smithy_schema::shape_id!("test", "Person"),
1930 aws_smithy_schema::ShapeType::String,
1931 "lastName",
1932 1,
1933 );
1934 static AGE: Schema<'static> = Schema::new_member(
1935 aws_smithy_schema::shape_id!("test", "Person"),
1936 aws_smithy_schema::ShapeType::Integer,
1937 "age",
1938 2,
1939 );
1940 static PERSON_SCHEMA: Schema<'static> = Schema::new_struct(
1941 aws_smithy_schema::shape_id!("test", "Person"),
1942 aws_smithy_schema::ShapeType::Structure,
1943 &[&FIRST_NAME, &LAST_NAME, &AGE],
1944 );
1945
1946 fn consume_person(
1947 person: &mut Person,
1948 schema: &Schema<'_>,
1949 deser: &mut dyn ShapeDeserializer,
1950 ) -> Result<(), SerdeError> {
1951 match schema.member_name() {
1952 Some("firstName") => person.first_name = deser.read_string(schema)?,
1953 Some("lastName") => person.last_name = deser.read_string(schema)?,
1954 Some("age") => person.age = deser.read_integer(schema)?,
1955 _ => {}
1956 }
1957 Ok(())
1958 }
1959
1960 let json = br#"{"lastName":"Smithy","firstName":"Alice","age":30}"#;
1961 let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
1962 let mut person = Person::default();
1963 deser
1964 .read_struct(&PERSON_SCHEMA, &mut |member, d| {
1965 consume_person(&mut person, member, d)
1966 })
1967 .unwrap();
1968 assert_eq!(
1969 person,
1970 Person {
1971 first_name: "Alice".to_string(),
1972 last_name: "Smithy".to_string(),
1973 age: 30
1974 }
1975 );
1976
1977 let json =
1978 br#"{"firstName": "Alice","age":12345678, "lastName":"\"Smithy\""}"#;
1979 let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
1980 let mut person = Person::default();
1981 deser
1982 .read_struct(&PERSON_SCHEMA, &mut |member, d| {
1983 consume_person(&mut person, member, d)
1984 })
1985 .unwrap();
1986 assert_eq!(
1987 person,
1988 Person {
1989 first_name: "Alice".to_string(),
1990 last_name: "\"Smithy\"".to_string(),
1991 age: 12345678
1992 }
1993 );
1994 }
1995
1996 #[test]
1997 fn test_read_list() {
1998 let json = b"[1, 2, 3, 4, 5]";
1999 let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
2000 let capacity = deser.container_size().unwrap_or(0);
2001 let mut result = Vec::with_capacity(capacity);
2002 let allocated_capacity = result.capacity();
2003 deser
2004 .read_list(dummy_schema(), &mut |deser| {
2005 result.push(deser.read_integer(dummy_schema())?);
2006 Ok(())
2007 })
2008 .unwrap();
2009 assert_eq!(result, vec![1, 2, 3, 4, 5]);
2010 assert_eq!(result.capacity(), allocated_capacity);
2012
2013 let json = b"[]";
2014 let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
2015 let capacity = deser.container_size().unwrap_or(0);
2016 let mut result = Vec::<i32>::with_capacity(capacity);
2017 let allocated_capacity = result.capacity();
2018 deser
2019 .read_list(dummy_schema(), &mut |deser| {
2020 result.push(deser.read_integer(dummy_schema())?);
2021 Ok(())
2022 })
2023 .unwrap();
2024 assert_eq!(result, Vec::<i32>::new());
2025 assert_eq!(result.capacity(), allocated_capacity);
2027
2028 let json = br#"["hello", "world"]"#;
2029 let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
2030 let capacity = deser.container_size().unwrap_or(0);
2031 let mut result = Vec::with_capacity(capacity);
2032 let allocated_capacity = result.capacity();
2033 deser
2034 .read_list(dummy_schema(), &mut |deser| {
2035 result.push(deser.read_string(dummy_schema())?);
2036 Ok(())
2037 })
2038 .unwrap();
2039 assert_eq!(result, vec!["hello", "world"]);
2040 assert_eq!(result.capacity(), allocated_capacity);
2042 }
2043
2044 #[test]
2045 fn test_container_size() {
2046 let deser =
2047 JsonDeserializer::new(b"[1, 2, 3, 4, 5]", Arc::new(JsonCodecSettings::default()));
2048 assert_eq!(deser.container_size(), Some(5));
2049
2050 let deser = JsonDeserializer::new(b"[]", Arc::new(JsonCodecSettings::default()));
2051 assert_eq!(deser.container_size(), Some(0));
2052
2053 let deser = JsonDeserializer::new(
2054 br#"{"a": 1, "b": 2, "c": 3}"#,
2055 Arc::new(JsonCodecSettings::default()),
2056 );
2057 assert_eq!(deser.container_size(), Some(3));
2058
2059 let deser = JsonDeserializer::new(b"{}", Arc::new(JsonCodecSettings::default()));
2060 assert_eq!(deser.container_size(), Some(0));
2061
2062 let deser = JsonDeserializer::new(
2063 b"[[1, 2], [3, 4], [5, 6]]",
2064 Arc::new(JsonCodecSettings::default()),
2065 );
2066 assert_eq!(deser.container_size(), Some(3));
2067
2068 let deser = JsonDeserializer::new(b"42", Arc::new(JsonCodecSettings::default()));
2069 assert_eq!(deser.container_size(), None);
2070 }
2071
2072 #[test]
2073 fn test_read_map() {
2074 use std::collections::HashMap;
2075
2076 let json = br#"{"a": 1, "b": 2, "c": 3}"#;
2077 let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
2078 let calculated_capacity = deser.container_size().unwrap_or(0);
2079 let mut result = HashMap::with_capacity(calculated_capacity);
2080 let allocated_capacity = result.capacity();
2081 deser
2082 .read_map(dummy_schema(), &mut |key, deser| {
2083 result.insert(key, deser.read_integer(dummy_schema())?);
2084 Ok(())
2085 })
2086 .unwrap();
2087 assert_eq!(result.len(), 3);
2088 assert_eq!(result.get("a"), Some(&1));
2089 assert_eq!(result.get("b"), Some(&2));
2090 assert_eq!(result.get("c"), Some(&3));
2091 assert_eq!(result.capacity(), allocated_capacity);
2093
2094 let json = b"{}";
2095 let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
2096 let calculated_capacity = deser.container_size().unwrap_or(0);
2097 let mut result = HashMap::<String, i32>::with_capacity(calculated_capacity);
2098 let allocated_capacity = result.capacity();
2099 deser
2100 .read_map(dummy_schema(), &mut |key, deser| {
2101 result.insert(key, deser.read_integer(dummy_schema())?);
2102 Ok(())
2103 })
2104 .unwrap();
2105 assert_eq!(result, HashMap::<String, i32>::new());
2106 assert_eq!(result.capacity(), allocated_capacity);
2108
2109 let json = br#"{"name": "Alice", "city": "Seattle"}"#;
2110 let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
2111 let calculated_capacity = deser.container_size().unwrap_or(0);
2112 let mut result = HashMap::with_capacity(calculated_capacity);
2113 let allocated_capacity = result.capacity();
2114 deser
2115 .read_map(dummy_schema(), &mut |key, deser| {
2116 result.insert(key, deser.read_string(dummy_schema())?);
2117 Ok(())
2118 })
2119 .unwrap();
2120 assert_eq!(result.len(), 2);
2121 assert_eq!(result.get("name"), Some(&"Alice".to_string()));
2122 assert_eq!(result.get("city"), Some(&"Seattle".to_string()));
2123 assert_eq!(result.capacity(), allocated_capacity);
2125 }
2126
2127 #[test]
2128 fn test_nested_complex_deserialization() {
2129 use aws_smithy_schema::Schema;
2130 use std::collections::HashMap;
2131
2132 #[derive(Debug, Default, PartialEq)]
2133 struct Address {
2134 street: String,
2135 city: String,
2136 zip: i32,
2137 }
2138 #[derive(Debug, Default, PartialEq)]
2139 struct Company {
2140 name: String,
2141 employees: Vec<String>,
2142 metadata: HashMap<String, i32>,
2143 active: bool,
2144 }
2145 #[derive(Debug, Default, PartialEq)]
2146 struct User {
2147 id: i64,
2148 name: String,
2149 scores: Vec<f64>,
2150 address: Address,
2151 companies: Vec<Company>,
2152 tags: HashMap<String, String>,
2153 }
2154
2155 static ADDR_STREET: Schema<'static> = Schema::new_member(
2157 aws_smithy_schema::shape_id!("test", "Address"),
2158 aws_smithy_schema::ShapeType::String,
2159 "street",
2160 0,
2161 );
2162 static ADDR_CITY: Schema<'static> = Schema::new_member(
2163 aws_smithy_schema::shape_id!("test", "Address"),
2164 aws_smithy_schema::ShapeType::String,
2165 "city",
2166 1,
2167 );
2168 static ADDR_ZIP: Schema<'static> = Schema::new_member(
2169 aws_smithy_schema::shape_id!("test", "Address"),
2170 aws_smithy_schema::ShapeType::Integer,
2171 "zip",
2172 2,
2173 );
2174 static ADDRESS_SCHEMA: Schema<'static> = Schema::new_struct(
2175 aws_smithy_schema::shape_id!("test", "Address"),
2176 aws_smithy_schema::ShapeType::Structure,
2177 &[&ADDR_STREET, &ADDR_CITY, &ADDR_ZIP],
2178 );
2179
2180 static COMP_NAME: Schema<'static> = Schema::new_member(
2182 aws_smithy_schema::shape_id!("test", "Company"),
2183 aws_smithy_schema::ShapeType::String,
2184 "name",
2185 0,
2186 );
2187 static COMP_EMPLOYEES: Schema<'static> = Schema::new_member(
2188 aws_smithy_schema::shape_id!("test", "Company"),
2189 aws_smithy_schema::ShapeType::List,
2190 "employees",
2191 1,
2192 );
2193 static COMP_METADATA: Schema<'static> = Schema::new_member(
2194 aws_smithy_schema::shape_id!("test", "Company"),
2195 aws_smithy_schema::ShapeType::Map,
2196 "metadata",
2197 2,
2198 );
2199 static COMP_ACTIVE: Schema<'static> = Schema::new_member(
2200 aws_smithy_schema::shape_id!("test", "Company"),
2201 aws_smithy_schema::ShapeType::Boolean,
2202 "active",
2203 3,
2204 );
2205 static COMPANY_SCHEMA: Schema<'static> = Schema::new_struct(
2206 aws_smithy_schema::shape_id!("test", "Company"),
2207 aws_smithy_schema::ShapeType::Structure,
2208 &[&COMP_NAME, &COMP_EMPLOYEES, &COMP_METADATA, &COMP_ACTIVE],
2209 );
2210
2211 static USER_ID: Schema<'static> = Schema::new_member(
2213 aws_smithy_schema::shape_id!("test", "User"),
2214 aws_smithy_schema::ShapeType::Long,
2215 "id",
2216 0,
2217 );
2218 static USER_NAME: Schema<'static> = Schema::new_member(
2219 aws_smithy_schema::shape_id!("test", "User"),
2220 aws_smithy_schema::ShapeType::String,
2221 "name",
2222 1,
2223 );
2224 static USER_SCORES: Schema<'static> = Schema::new_member(
2225 aws_smithy_schema::shape_id!("test", "User"),
2226 aws_smithy_schema::ShapeType::List,
2227 "scores",
2228 2,
2229 );
2230 static USER_ADDRESS: Schema<'static> = Schema::new_member(
2231 aws_smithy_schema::shape_id!("test", "User"),
2232 aws_smithy_schema::ShapeType::Structure,
2233 "address",
2234 3,
2235 );
2236 static USER_COMPANIES: Schema<'static> = Schema::new_member(
2237 aws_smithy_schema::shape_id!("test", "User"),
2238 aws_smithy_schema::ShapeType::List,
2239 "companies",
2240 4,
2241 );
2242 static USER_TAGS: Schema<'static> = Schema::new_member(
2243 aws_smithy_schema::shape_id!("test", "User"),
2244 aws_smithy_schema::ShapeType::Map,
2245 "tags",
2246 5,
2247 );
2248 static USER_SCHEMA: Schema<'static> = Schema::new_struct(
2249 aws_smithy_schema::shape_id!("test", "User"),
2250 aws_smithy_schema::ShapeType::Structure,
2251 &[
2252 &USER_ID,
2253 &USER_NAME,
2254 &USER_SCORES,
2255 &USER_ADDRESS,
2256 &USER_COMPANIES,
2257 &USER_TAGS,
2258 ],
2259 );
2260
2261 fn consume_address(
2262 addr: &mut Address,
2263 schema: &Schema<'_>,
2264 deser: &mut dyn ShapeDeserializer,
2265 ) -> Result<(), SerdeError> {
2266 match schema.member_name() {
2267 Some("street") => addr.street = deser.read_string(schema)?,
2268 Some("city") => addr.city = deser.read_string(schema)?,
2269 Some("zip") => addr.zip = deser.read_integer(schema)?,
2270 _ => {}
2271 }
2272 Ok(())
2273 }
2274
2275 fn consume_company(
2276 comp: &mut Company,
2277 schema: &Schema<'_>,
2278 deser: &mut dyn ShapeDeserializer,
2279 ) -> Result<(), SerdeError> {
2280 match schema.member_name() {
2281 Some("name") => comp.name = deser.read_string(schema)?,
2282 Some("active") => comp.active = deser.read_boolean(schema)?,
2283 Some("employees") => {
2284 let mut v = Vec::new();
2285 deser.read_list(schema, &mut |d| {
2286 v.push(d.read_string(dummy_schema())?);
2287 Ok(())
2288 })?;
2289 comp.employees = v;
2290 }
2291 Some("metadata") => {
2292 let mut m = HashMap::new();
2293 deser.read_map(schema, &mut |k, d| {
2294 m.insert(k, d.read_integer(dummy_schema())?);
2295 Ok(())
2296 })?;
2297 comp.metadata = m;
2298 }
2299 _ => {}
2300 }
2301 Ok(())
2302 }
2303
2304 fn consume_user(
2305 user: &mut User,
2306 schema: &Schema<'_>,
2307 deser: &mut dyn ShapeDeserializer,
2308 ) -> Result<(), SerdeError> {
2309 match schema.member_name() {
2310 Some("id") => user.id = deser.read_long(schema)?,
2311 Some("name") => user.name = deser.read_string(schema)?,
2312 Some("scores") => {
2313 let mut v = Vec::new();
2314 deser.read_list(schema, &mut |d| {
2315 v.push(d.read_double(dummy_schema())?);
2316 Ok(())
2317 })?;
2318 user.scores = v;
2319 }
2320 Some("address") => {
2321 let mut addr = Address::default();
2322 deser.read_struct(&ADDRESS_SCHEMA, &mut |member, d| {
2323 consume_address(&mut addr, member, d)
2324 })?;
2325 user.address = addr;
2326 }
2327 Some("companies") => {
2328 let mut v = Vec::new();
2329 deser.read_list(schema, &mut |d| {
2330 let mut comp = Company::default();
2331 d.read_struct(&COMPANY_SCHEMA, &mut |member, d| {
2332 consume_company(&mut comp, member, d)
2333 })?;
2334 v.push(comp);
2335 Ok(())
2336 })?;
2337 user.companies = v;
2338 }
2339 Some("tags") => {
2340 let mut m = HashMap::new();
2341 deser.read_map(schema, &mut |k, d| {
2342 m.insert(k, d.read_string(dummy_schema())?);
2343 Ok(())
2344 })?;
2345 user.tags = m;
2346 }
2347 _ => {}
2348 }
2349 Ok(())
2350 }
2351
2352 let json = br#"{
2353 "id": 12345,
2354 "name": "John Doe",
2355 "scores": [95.5, 87.3, 92.1],
2356 "address": {
2357 "street": "123 Main St",
2358 "city": "Seattle",
2359 "zip": 98101
2360 },
2361 "companies": [
2362 {
2363 "name": "TechCorp",
2364 "employees": ["Alice", "Bob"],
2365 "metadata": {"founded": 2010, "size": 500},
2366 "active": true
2367 },
2368 {
2369 "name": "StartupInc",
2370 "employees": ["Charlie"],
2371 "metadata": {"founded": 2020},
2372 "active": false
2373 }
2374 ],
2375 "tags": {"role": "admin", "level": "senior"}
2376 }"#;
2377
2378 let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
2379 let mut user = User::default();
2380 deser
2381 .read_struct(&USER_SCHEMA, &mut |member, d| {
2382 consume_user(&mut user, member, d)
2383 })
2384 .unwrap();
2385
2386 assert_eq!(user.id, 12345);
2387 assert_eq!(user.name, "John Doe");
2388 assert_eq!(user.scores, vec![95.5, 87.3, 92.1]);
2389 assert_eq!(user.address.street, "123 Main St");
2390 assert_eq!(user.address.city, "Seattle");
2391 assert_eq!(user.address.zip, 98101);
2392 assert_eq!(user.companies.len(), 2);
2393 assert_eq!(user.companies[0].name, "TechCorp");
2394 assert_eq!(user.companies[0].employees, vec!["Alice", "Bob"]);
2395 assert_eq!(user.companies[0].metadata.get("founded"), Some(&2010));
2396 assert_eq!(user.companies[0].metadata.get("size"), Some(&500));
2397 assert!(user.companies[0].active);
2398 assert_eq!(user.companies[1].name, "StartupInc");
2399 assert_eq!(user.companies[1].employees, vec!["Charlie"]);
2400 assert_eq!(user.companies[1].metadata.get("founded"), Some(&2020));
2401 assert!(!user.companies[1].active);
2402 assert_eq!(user.tags.get("role"), Some(&"admin".to_string()));
2403 assert_eq!(user.tags.get("level"), Some(&"senior".to_string()));
2404 }
2405
2406 #[test]
2407 fn test_json_name_deserialization() {
2408 use aws_smithy_schema::Schema;
2409
2410 static FOO_MEMBER: Schema<'static> = Schema::new_member(
2411 aws_smithy_schema::shape_id!("test", "MyStruct"),
2412 aws_smithy_schema::ShapeType::String,
2413 "foo",
2414 0,
2415 );
2416 static BAR_MEMBER: Schema<'static> = Schema::new_member(
2418 aws_smithy_schema::shape_id!("test", "MyStruct"),
2419 aws_smithy_schema::ShapeType::Integer,
2420 "bar",
2421 1,
2422 )
2423 .with_json_name("Baz");
2424 static STRUCT_SCHEMA: Schema<'static> = Schema::new_struct(
2425 aws_smithy_schema::shape_id!("test", "MyStruct"),
2426 aws_smithy_schema::ShapeType::Structure,
2427 &[&FOO_MEMBER, &BAR_MEMBER],
2428 );
2429
2430 let json = br#"{"foo":"hello","Baz":42}"#;
2431
2432 let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
2434 let (mut foo, mut bar) = (None::<String>, None::<i32>);
2435 deser
2436 .read_struct(&STRUCT_SCHEMA, &mut |member, d| {
2437 match member.member_name() {
2438 Some("foo") => foo = Some(d.read_string(member)?),
2439 Some("bar") => bar = Some(d.read_integer(member)?),
2440 _ => {}
2441 }
2442 Ok(())
2443 })
2444 .unwrap();
2445 assert_eq!(foo.as_deref(), Some("hello"));
2446 assert_eq!(bar, Some(42));
2447
2448 let mut deser = JsonDeserializer::new(
2450 json,
2451 Arc::new(JsonCodecSettings::builder().use_json_name(false).build()),
2452 );
2453 let (mut foo, mut bar) = (None::<String>, None::<i32>);
2454 deser
2455 .read_struct(&STRUCT_SCHEMA, &mut |member, d| {
2456 match member.member_name() {
2457 Some("foo") => foo = Some(d.read_string(member)?),
2458 Some("bar") => bar = Some(d.read_integer(member)?),
2459 _ => {}
2460 }
2461 Ok(())
2462 })
2463 .unwrap();
2464 assert_eq!(foo.as_deref(), Some("hello"));
2465 assert_eq!(bar, None); }
2467
2468 #[test]
2472 fn colliding_wire_names_resolve_to_the_first_member() {
2473 use aws_smithy_schema::{shape_id, Schema, ShapeType};
2474
2475 static STATUS: Schema<'static> = Schema::new_member(
2476 shape_id!("ns", "S", "status"),
2477 ShapeType::String,
2478 "status",
2479 0,
2480 );
2481 static STATUS_TEXT: Schema<'static> = Schema::new_member(
2482 shape_id!("ns", "S", "statusText"),
2483 ShapeType::String,
2484 "statusText",
2485 1,
2486 )
2487 .with_json_name("status");
2488 static OTHER: Schema<'static> =
2489 Schema::new_member(shape_id!("ns", "S", "other"), ShapeType::String, "other", 2);
2490 static S: Schema<'static> = Schema::new_struct(
2491 shape_id!("ns", "S"),
2492 ShapeType::Structure,
2493 &[&STATUS, &STATUS_TEXT, &OTHER],
2494 );
2495
2496 fn read(json: &[u8]) -> Vec<(usize, String)> {
2497 let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
2498 let mut seen = Vec::new();
2499 deser
2500 .read_struct(&S, &mut |member, d| {
2501 seen.push((member.member_index().unwrap(), d.read_string(member)?));
2502 Ok(())
2503 })
2504 .unwrap();
2505 seen
2506 }
2507
2508 assert_eq!(
2509 read(br#"{"status":"a","status":"b"}"#),
2510 [(0, "a".to_owned()), (0, "b".to_owned())]
2511 );
2512 assert_eq!(
2513 read(br#"{"other":"x","status":"a"}"#),
2514 [(2, "x".to_owned()), (0, "a".to_owned())]
2515 );
2516 assert_eq!(
2517 read(br#"{"status":"a","other":"x","status":"b"}"#),
2518 [
2519 (0, "a".to_owned()),
2520 (2, "x".to_owned()),
2521 (0, "b".to_owned())
2522 ]
2523 );
2524 }
2525
2526 fn timestamp_schema() -> &'static aws_smithy_schema::Schema<'static> {
2527 &aws_smithy_schema::prelude::TIMESTAMP
2528 }
2529
2530 #[test]
2531 fn test_read_timestamp_positive_integer() {
2532 let mut deser =
2533 JsonDeserializer::new(b"1700000000", Arc::new(JsonCodecSettings::default()));
2534 let ts = deser.read_timestamp(timestamp_schema()).unwrap();
2535 assert_eq!(ts, DateTime::from_secs(1700000000));
2536 }
2537
2538 #[test]
2539 fn test_read_timestamp_negative_integer() {
2540 let mut deser = JsonDeserializer::new(b"-1000", Arc::new(JsonCodecSettings::default()));
2541 let ts = deser.read_timestamp(timestamp_schema()).unwrap();
2542 assert_eq!(ts, DateTime::from_secs(-1000));
2543 }
2544
2545 #[test]
2546 fn test_read_timestamp_float() {
2547 let mut deser =
2549 JsonDeserializer::new(b"1.615218678973E9", Arc::new(JsonCodecSettings::default()));
2550 let ts = deser.read_timestamp(timestamp_schema()).unwrap();
2551 assert_eq!(ts, DateTime::from_secs_f64(1.615218678973E9));
2552 }
2553
2554 #[test]
2555 fn test_read_timestamp_float_simple() {
2556 let mut deser =
2557 JsonDeserializer::new(b"1700000000.5", Arc::new(JsonCodecSettings::default()));
2558 let ts = deser.read_timestamp(timestamp_schema()).unwrap();
2559 assert_eq!(ts, DateTime::from_secs_f64(1700000000.5));
2560 }
2561
2562 #[test]
2563 fn test_read_timestamp_string_datetime() {
2564 let mut deser = JsonDeserializer::new(
2565 br#""2023-11-14T22:13:20Z""#,
2566 Arc::new(JsonCodecSettings::default()),
2567 );
2568 let ts = deser.read_timestamp(timestamp_schema()).unwrap();
2569 assert_eq!(ts, DateTime::from_secs(1700000000));
2570 }
2571
2572 #[test]
2573 fn test_read_timestamp_invalid() {
2574 let mut deser = JsonDeserializer::new(b"true", Arc::new(JsonCodecSettings::default()));
2575 assert!(deser.read_timestamp(timestamp_schema()).is_err());
2576 }
2577
2578 #[test]
2579 fn test_read_timestamp_string_honors_httpdate_default() {
2580 let settings = JsonCodecSettings::builder()
2585 .default_timestamp_format(aws_smithy_types::date_time::Format::HttpDate)
2586 .build();
2587 let mut deser =
2588 JsonDeserializer::new(br#""Tue, 14 Nov 2023 22:13:20 GMT""#, Arc::new(settings));
2589 let ts = deser.read_timestamp(timestamp_schema()).unwrap();
2590 assert_eq!(ts, DateTime::from_secs(1700000000));
2591 }
2592
2593 #[test]
2594 fn test_read_timestamp_string_default_epoch_still_parses_datetime() {
2595 let mut deser = JsonDeserializer::new(
2600 br#""2023-11-14T22:13:20Z""#,
2601 Arc::new(JsonCodecSettings::default()),
2602 );
2603 let ts = deser.read_timestamp(timestamp_schema()).unwrap();
2604 assert_eq!(ts, DateTime::from_secs(1700000000));
2605 }
2606
2607 #[test]
2608 fn test_read_timestamp_rejects_u64_overflow() {
2609 let big = (i64::MAX as u64 + 1).to_string();
2612 let mut deser =
2613 JsonDeserializer::new(big.as_bytes(), Arc::new(JsonCodecSettings::default()));
2614 let err = deser
2615 .read_timestamp(timestamp_schema())
2616 .expect_err("u64 epoch overflowing i64 must error");
2617 assert!(
2618 matches!(err, SerdeError::InvalidInput { .. }),
2619 "expected InvalidInput, got {err:?}"
2620 );
2621 }
2622
2623 #[test]
2624 fn test_read_integer_rejects_float() {
2625 let mut deser = JsonDeserializer::new(b"1.5", Arc::new(JsonCodecSettings::default()));
2628 let err = deser
2629 .read_integer(&aws_smithy_schema::prelude::INTEGER)
2630 .expect_err("a float must not deserialize as an integer");
2631 assert!(
2632 matches!(err, SerdeError::TypeMismatch { .. }),
2633 "expected TypeMismatch, got {err:?}"
2634 );
2635 }
2636
2637 #[test]
2638 fn test_skip_value_empty_array() {
2639 use aws_smithy_schema::ShapeType;
2641 static KNOWN_MEMBER: Schema<'static> = Schema::new_member(
2642 aws_smithy_schema::shape_id!("test", "S"),
2643 ShapeType::String,
2644 "known",
2645 0,
2646 );
2647 static MEMBERS: &[&Schema<'_>] = &[&KNOWN_MEMBER];
2648 static TEST_SCHEMA: Schema<'static> = Schema::new_struct(
2649 aws_smithy_schema::shape_id!("test", "S"),
2650 ShapeType::Structure,
2651 MEMBERS,
2652 );
2653
2654 let json = br#"{"known":"yes","Items":[],"extra":true}"#;
2655 let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
2656 let mut known_val = String::new();
2657 deser
2658 .read_struct(&TEST_SCHEMA, &mut |member, deser| {
2659 if member.member_name() == Some("known") {
2660 known_val = deser.read_string(dummy_schema())?;
2661 }
2662 Ok(())
2663 })
2664 .unwrap();
2665 assert_eq!(known_val, "yes");
2666 }
2667
2668 #[test]
2669 fn test_skip_value_nested_objects() {
2670 use aws_smithy_schema::ShapeType;
2671 static D_MEMBER: Schema<'static> = Schema::new_member(
2672 aws_smithy_schema::shape_id!("test", "S"),
2673 ShapeType::String,
2674 "d",
2675 0,
2676 );
2677 static MEMBERS: &[&Schema<'_>] = &[&D_MEMBER];
2678 static TEST_SCHEMA: Schema<'static> = Schema::new_struct(
2679 aws_smithy_schema::shape_id!("test", "S"),
2680 ShapeType::Structure,
2681 MEMBERS,
2682 );
2683
2684 let json = br#"{"a":{"b":[1,2,{"c":3}]},"d":"ok"}"#;
2685 let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
2686 let mut d_val = String::new();
2687 deser
2688 .read_struct(&TEST_SCHEMA, &mut |member, deser| {
2689 if member.member_name() == Some("d") {
2690 d_val = deser.read_string(dummy_schema())?;
2691 }
2692 Ok(())
2693 })
2694 .unwrap();
2695 assert_eq!(d_val, "ok");
2696 }
2697
2698 #[test]
2704 fn regression_truncated_list_does_not_infinite_loop() {
2705 let mut deser = JsonDeserializer::new(b"[", Arc::new(JsonCodecSettings::default()));
2709 let err = deser
2710 .read_list(dummy_schema(), &mut |d| {
2711 d.read_integer(dummy_schema()).map(|_| ())
2712 })
2713 .expect_err("truncated list input must be rejected");
2714 assert!(
2715 matches!(err, SerdeError::InvalidInput { .. }),
2716 "expected InvalidInput, got {err:?}"
2717 );
2718 }
2719
2720 #[test]
2721 fn regression_truncated_string_list_does_not_infinite_loop() {
2722 let mut deser = JsonDeserializer::new(b"[", Arc::new(JsonCodecSettings::default()));
2725 let err = deser
2726 .read_string_list(dummy_schema())
2727 .expect_err("truncated string list input must be rejected");
2728 assert!(
2729 matches!(err, SerdeError::InvalidInput { .. }),
2730 "expected InvalidInput, got {err:?}"
2731 );
2732 }
2733
2734 #[test]
2735 fn regression_truncated_document_array_does_not_infinite_loop() {
2736 let mut deser = JsonDeserializer::new(b"[", Arc::new(JsonCodecSettings::default()));
2738 let err = deser
2739 .read_document(dummy_schema())
2740 .expect_err("truncated document array must be rejected");
2741 assert!(
2742 matches!(err, SerdeError::InvalidInput { .. }),
2743 "expected InvalidInput, got {err:?}"
2744 );
2745 }
2746
2747 #[test]
2748 fn regression_unterminated_object_key_does_not_panic() {
2749 use aws_smithy_schema::Schema;
2753
2754 static MEMBER: Schema<'static> = Schema::new_member(
2755 aws_smithy_schema::shape_id!("test", "S"),
2756 aws_smithy_schema::ShapeType::String,
2757 "m",
2758 0,
2759 );
2760 static SCHEMA: Schema<'static> = Schema::new_struct(
2761 aws_smithy_schema::shape_id!("test", "S"),
2762 aws_smithy_schema::ShapeType::Structure,
2763 &[&MEMBER],
2764 );
2765
2766 let mut deser = JsonDeserializer::new(br#"{""#, Arc::new(JsonCodecSettings::default()));
2767 let err = deser
2768 .read_struct(&SCHEMA, &mut |_, _| Ok(()))
2769 .expect_err("unterminated object key must be rejected");
2770 assert!(
2771 matches!(err, SerdeError::InvalidInput { .. }),
2772 "expected InvalidInput, got {err:?}"
2773 );
2774 }
2775
2776 #[test]
2777 fn regression_skip_value_truncated_true_does_not_panic() {
2778 use aws_smithy_schema::Schema;
2782
2783 static MEMBER: Schema<'static> = Schema::new_member(
2784 aws_smithy_schema::shape_id!("test", "S"),
2785 aws_smithy_schema::ShapeType::String,
2786 "known",
2787 0,
2788 );
2789 static SCHEMA: Schema<'static> = Schema::new_struct(
2790 aws_smithy_schema::shape_id!("test", "S"),
2791 aws_smithy_schema::ShapeType::Structure,
2792 &[&MEMBER],
2793 );
2794
2795 let input = br#"{"":t"#;
2799 let mut deser = JsonDeserializer::new(input, Arc::new(JsonCodecSettings::default()));
2800 let err = deser
2801 .read_struct(&SCHEMA, &mut |_, _| Ok(()))
2802 .expect_err("truncated `true` literal must be rejected");
2803 assert!(
2804 matches!(err, SerdeError::InvalidInput { .. }),
2805 "expected InvalidInput, got {err:?}"
2806 );
2807 }
2808
2809 #[test]
2810 fn regression_skip_value_rejects_malformed_true_literal() {
2811 use aws_smithy_schema::Schema;
2816
2817 static MEMBER: Schema<'static> = Schema::new_member(
2818 aws_smithy_schema::shape_id!("test", "S"),
2819 aws_smithy_schema::ShapeType::String,
2820 "known",
2821 0,
2822 );
2823 static SCHEMA: Schema<'static> = Schema::new_struct(
2824 aws_smithy_schema::shape_id!("test", "S"),
2825 aws_smithy_schema::ShapeType::Structure,
2826 &[&MEMBER],
2827 );
2828
2829 for bad in [
2830 &br#"{"":t!!!}"#[..],
2831 &br#"{"":f!!!!}"#[..],
2832 &br#"{"":n!!!}"#[..],
2833 ] {
2834 let mut deser = JsonDeserializer::new(bad, Arc::new(JsonCodecSettings::default()));
2835 let result = deser.read_struct(&SCHEMA, &mut |_, _| Ok(()));
2836 assert!(
2837 matches!(result, Err(SerdeError::InvalidInput { .. })),
2838 "expected InvalidInput for malformed input {:?}, got {:?}",
2839 std::str::from_utf8(bad).unwrap_or("<non-utf8>"),
2840 result
2841 );
2842 }
2843 }
2844
2845 #[test]
2846 fn regression_truncated_struct_does_not_infinite_loop() {
2847 use aws_smithy_schema::Schema;
2852
2853 static MEMBER: Schema<'static> = Schema::new_member(
2854 aws_smithy_schema::shape_id!("test", "S"),
2855 aws_smithy_schema::ShapeType::String,
2856 "m",
2857 0,
2858 );
2859 static SCHEMA: Schema<'static> = Schema::new_struct(
2860 aws_smithy_schema::shape_id!("test", "S"),
2861 aws_smithy_schema::ShapeType::Structure,
2862 &[&MEMBER],
2863 );
2864
2865 let mut deser = JsonDeserializer::new(b"{", Arc::new(JsonCodecSettings::default()));
2866 let err = deser
2867 .read_struct(&SCHEMA, &mut |_, _| Ok(()))
2868 .expect_err("truncated struct input must be rejected");
2869 assert!(
2870 matches!(err, SerdeError::InvalidInput { .. }),
2871 "expected InvalidInput, got {err:?}"
2872 );
2873 }
2874
2875 #[test]
2876 fn regression_truncated_map_does_not_infinite_loop() {
2877 let mut deser = JsonDeserializer::new(b"{", Arc::new(JsonCodecSettings::default()));
2879 let err = deser
2880 .read_map(dummy_schema(), &mut |_, _| Ok(()))
2881 .expect_err("truncated map input must be rejected");
2882 assert!(
2883 matches!(err, SerdeError::InvalidInput { .. }),
2884 "expected InvalidInput, got {err:?}"
2885 );
2886 }
2887
2888 fn build_nested(open: &str, close: &str, n: usize) -> Vec<u8> {
2900 build_nested_with_inner(open, "", close, n)
2901 }
2902
2903 fn build_nested_with_inner(open: &str, inner: &str, close: &str, n: usize) -> Vec<u8> {
2906 let mut out = String::with_capacity(open.len() * n + inner.len() + close.len() * n);
2907 for _ in 0..n {
2908 out.push_str(open);
2909 }
2910 out.push_str(inner);
2911 for _ in 0..n {
2912 out.push_str(close);
2913 }
2914 out.into_bytes()
2915 }
2916
2917 fn recursive_struct_schema() -> &'static Schema<'static> {
2922 static MEMBER_A: Schema<'static> = Schema::new_member(
2923 aws_smithy_schema::shape_id!("test", "Rec"),
2924 aws_smithy_schema::ShapeType::Structure,
2925 "a",
2926 0,
2927 );
2928 static RECURSIVE: Schema<'static> = Schema::new_struct(
2929 aws_smithy_schema::shape_id!("test", "Rec"),
2930 aws_smithy_schema::ShapeType::Structure,
2931 &[&MEMBER_A],
2932 );
2933 &RECURSIVE
2934 }
2935
2936 fn recursive_struct_consumer(
2938 _member: &Schema<'_>,
2939 deser: &mut dyn ShapeDeserializer,
2940 ) -> Result<(), SerdeError> {
2941 deser.read_struct(recursive_struct_schema(), &mut recursive_struct_consumer)
2942 }
2943
2944 fn recursive_list_consumer(deser: &mut dyn ShapeDeserializer) -> Result<(), SerdeError> {
2946 deser.read_list(dummy_schema(), &mut recursive_list_consumer)
2947 }
2948
2949 fn recursive_map_consumer(
2951 _key: String,
2952 deser: &mut dyn ShapeDeserializer,
2953 ) -> Result<(), SerdeError> {
2954 deser.read_map(dummy_schema(), &mut recursive_map_consumer)
2955 }
2956
2957 fn assert_depth_error(err: SerdeError) {
2958 match err {
2959 SerdeError::Custom { ref message, .. } => {
2960 assert!(
2961 message.contains("maximum nesting depth exceeded"),
2962 "wrong error message: {message}"
2963 );
2964 }
2965 other => panic!("expected Custom depth error, got {other:?}"),
2966 }
2967 }
2968
2969 #[test]
2970 fn depth_limit_read_struct_rejects_deeply_nested() {
2971 let payload = build_nested(r#"{"a":"#, "}", 200);
2975 let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
2976 let err = deser
2977 .read_struct(recursive_struct_schema(), &mut recursive_struct_consumer)
2978 .expect_err("deeply nested struct must be rejected");
2979 assert_depth_error(err);
2980 }
2981
2982 #[test]
2983 fn depth_limit_read_struct_accepts_under_limit() {
2984 let payload = build_nested_with_inner(r#"{"a":"#, "{}", "}", 100);
2987 let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
2988 deser
2989 .read_struct(recursive_struct_schema(), &mut recursive_struct_consumer)
2990 .expect("100-level nesting should succeed");
2991 }
2992
2993 #[test]
2994 fn depth_limit_read_list_rejects_deeply_nested() {
2995 let payload = build_nested("[", "]", 200);
2996 let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
2997 let err = deser
2998 .read_list(dummy_schema(), &mut recursive_list_consumer)
2999 .expect_err("deeply nested list must be rejected");
3000 assert_depth_error(err);
3001 }
3002
3003 #[test]
3004 fn depth_limit_read_list_accepts_under_limit() {
3005 let payload = build_nested("[", "]", 100);
3007 let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
3008 deser
3009 .read_list(dummy_schema(), &mut recursive_list_consumer)
3010 .expect("100-level nesting should succeed");
3011 }
3012
3013 #[test]
3014 fn depth_limit_read_map_rejects_deeply_nested() {
3015 let payload = build_nested(r#"{"k":"#, "}", 200);
3016 let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
3017 let err = deser
3018 .read_map(dummy_schema(), &mut recursive_map_consumer)
3019 .expect_err("deeply nested map must be rejected");
3020 assert_depth_error(err);
3021 }
3022
3023 #[test]
3024 fn depth_limit_read_map_accepts_under_limit() {
3025 let payload = build_nested_with_inner(r#"{"k":"#, "{}", "}", 100);
3026 let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
3027 deser
3028 .read_map(dummy_schema(), &mut recursive_map_consumer)
3029 .expect("100-level nesting should succeed");
3030 }
3031
3032 #[test]
3033 fn depth_limit_read_document_rejects_deeply_nested_object() {
3034 let payload = build_nested(r#"{"k":"#, "}", 200);
3037 let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
3038 let err = deser
3039 .read_document(dummy_schema())
3040 .expect_err("deeply nested document must be rejected");
3041 assert_depth_error(err);
3042 }
3043
3044 #[test]
3045 fn depth_limit_read_document_rejects_deeply_nested_array() {
3046 let payload = build_nested("[", "]", 200);
3047 let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
3048 let err = deser
3049 .read_document(dummy_schema())
3050 .expect_err("deeply nested document array must be rejected");
3051 assert_depth_error(err);
3052 }
3053
3054 #[test]
3055 fn depth_limit_read_document_accepts_under_limit() {
3056 let payload = build_nested_with_inner(r#"{"k":"#, "{}", "}", 100);
3057 let mut deser = JsonDeserializer::new(&payload, Arc::new(JsonCodecSettings::default()));
3058 deser
3059 .read_document(dummy_schema())
3060 .expect("100-level nesting should succeed");
3061 }
3062
3063 #[test]
3064 fn depth_limit_respects_custom_max_depth_setting() {
3065 let settings = JsonCodecSettings::builder().max_depth(16).build();
3067
3068 let payload = build_nested("[", "]", 20);
3070 let mut deser = JsonDeserializer::new(&payload, Arc::new(settings));
3071 let err = deser
3072 .read_list(dummy_schema(), &mut recursive_list_consumer)
3073 .expect_err("20-level nesting must exceed custom limit of 16");
3074 assert_depth_error(err);
3075
3076 let settings_ok = JsonCodecSettings::builder().max_depth(16).build();
3078 let payload_ok = build_nested("[", "]", 10);
3079 let mut deser_ok = JsonDeserializer::new(&payload_ok, Arc::new(settings_ok));
3080 deser_ok
3081 .read_list(dummy_schema(), &mut recursive_list_consumer)
3082 .expect("10-level nesting should succeed under custom limit");
3083 }
3084
3085 #[test]
3088 fn read_document_keeps_in_range_int_as_pos_int() {
3089 let mut deser = JsonDeserializer::new(b"42", Arc::new(JsonCodecSettings::default()));
3092 match deser.read_document(dummy_schema()).unwrap() {
3093 Document::Number(Number::PosInt(n)) => assert_eq!(n, 42),
3094 other => panic!("expected Number::PosInt(42), got {other:?}"),
3095 }
3096 }
3097
3098 #[test]
3099 fn read_document_keeps_in_range_negative_int_as_neg_int() {
3100 let mut deser = JsonDeserializer::new(b"-123", Arc::new(JsonCodecSettings::default()));
3101 match deser.read_document(dummy_schema()).unwrap() {
3102 Document::Number(Number::NegInt(n)) => assert_eq!(n, -123),
3103 other => panic!("expected Number::NegInt(-123), got {other:?}"),
3104 }
3105 }
3106
3107 #[test]
3108 fn read_document_keeps_finite_float_as_number_float() {
3109 let mut deser = JsonDeserializer::new(b"1.5", Arc::new(JsonCodecSettings::default()));
3110 match deser.read_document(dummy_schema()).unwrap() {
3111 Document::Number(Number::Float(f)) => assert_eq!(f, 1.5),
3112 other => panic!("expected Number::Float(1.5), got {other:?}"),
3113 }
3114 }
3115
3116 #[test]
3120 fn read_document_reads_signed_exponents() {
3121 for strict in [false, true] {
3122 let settings = Arc::new(
3123 JsonCodecSettings::builder()
3124 .enforce_strictness(strict)
3125 .build(),
3126 );
3127 for (input, expected) in [
3128 ("1e-5", 1e-5),
3129 ("-2e-1", -2e-1),
3130 ("1.5E-3", 1.5e-3),
3131 ("1e-0", 1.0),
3132 ("1e+2", 100.0),
3133 ("1E+2", 100.0),
3134 ("-0.0", -0.0),
3135 ] {
3136 let mut deser = JsonDeserializer::new(input.as_bytes(), settings.clone());
3137 match deser.read_document(dummy_schema()) {
3138 Ok(Document::Number(Number::Float(f))) => {
3139 assert_eq!(f, expected, "{input} (strict={strict})");
3140 assert_eq!(f.is_sign_negative(), expected.is_sign_negative(), "{input}");
3141 }
3142 other => panic!("{input} (strict={strict}): expected Float, got {other:?}"),
3143 }
3144 }
3145 let mut deser = JsonDeserializer::new(b"[1e-5,-2E+1 ,3]", settings.clone());
3147 assert_eq!(
3148 deser.read_document(dummy_schema()).unwrap(),
3149 Document::Array(vec![
3150 Document::Number(Number::Float(1e-5)),
3151 Document::Number(Number::Float(-20.0)),
3152 Document::Number(Number::PosInt(3)),
3153 ]),
3154 "strict={strict}"
3155 );
3156 }
3157 let strict = Arc::new(
3159 JsonCodecSettings::builder()
3160 .enforce_strictness(true)
3161 .build(),
3162 );
3163 for bad in ["1e", "1e-", "1-2", "01", "1.", "1e+-2", "--1"] {
3164 let mut deser = JsonDeserializer::new(bad.as_bytes(), strict.clone());
3165 assert!(
3166 deser.read_document(dummy_schema()).is_err(),
3167 "strict mode accepted {bad:?}"
3168 );
3169 }
3170 }
3171
3172 #[test]
3173 fn read_document_falls_back_to_float_for_oversize_positive_int() {
3174 let bytes = b"99999999999999999999999";
3181 let mut deser = JsonDeserializer::new(bytes, Arc::new(JsonCodecSettings::default()));
3182 match deser.read_document(dummy_schema()).unwrap() {
3183 Document::Number(Number::Float(f)) => {
3184 assert_eq!(f, 99999999999999999999999f64);
3185 }
3186 other => panic!("expected Number::Float, got {other:?}"),
3187 }
3188 }
3189
3190 #[test]
3191 fn read_document_falls_back_to_float_for_oversize_negative_int() {
3192 let bytes = b"-99999999999999999999999";
3195 let mut deser = JsonDeserializer::new(bytes, Arc::new(JsonCodecSettings::default()));
3196 match deser.read_document(dummy_schema()).unwrap() {
3197 Document::Number(Number::Float(f)) => {
3198 assert_eq!(f, -99999999999999999999999f64);
3199 }
3200 other => panic!("expected Number::Float, got {other:?}"),
3201 }
3202 }
3203
3204 #[test]
3205 fn read_document_oversize_decimal_overflows_to_infinity() {
3206 let bytes = b"1e500";
3211 let mut deser = JsonDeserializer::new(bytes, Arc::new(JsonCodecSettings::default()));
3212 match deser.read_document(dummy_schema()).unwrap() {
3213 Document::Number(Number::Float(f)) => assert!(f.is_infinite() && f.is_sign_positive()),
3214 other => panic!("expected Number::Float(inf), got {other:?}"),
3215 }
3216
3217 let mut deser = JsonDeserializer::new(bytes, Arc::new(JsonCodecSettings::default()));
3219 let bd = deser.read_big_decimal(dummy_schema()).unwrap();
3220 assert!(
3221 !bd.as_ref().to_lowercase().contains("inf"),
3222 "expected a finite arbitrary-precision repr, got {}",
3223 bd.as_ref()
3224 );
3225 }
3226
3227 #[test]
3228 fn read_document_keeps_max_u64_as_pos_int() {
3229 let bytes = b"18446744073709551615";
3232 let mut deser = JsonDeserializer::new(bytes, Arc::new(JsonCodecSettings::default()));
3233 match deser.read_document(dummy_schema()).unwrap() {
3234 Document::Number(Number::PosInt(n)) => assert_eq!(n, u64::MAX),
3235 other => panic!("expected Number::PosInt(u64::MAX), got {other:?}"),
3236 }
3237 }
3238
3239 #[test]
3240 fn read_document_falls_back_just_over_u64_max() {
3241 let bytes = b"18446744073709551616";
3243 let mut deser = JsonDeserializer::new(bytes, Arc::new(JsonCodecSettings::default()));
3244 match deser.read_document(dummy_schema()).unwrap() {
3245 Document::Number(Number::Float(f)) => assert_eq!(f, 18446744073709551616f64),
3246 other => panic!("expected Number::Float, got {other:?}"),
3247 }
3248 }
3249
3250 #[test]
3253 fn read_big_integer_accepts_json_number() {
3254 let bytes = b"99999999999999999999999";
3255 let mut deser = JsonDeserializer::new(bytes, Arc::new(JsonCodecSettings::default()));
3256 let bi = deser.read_big_integer(dummy_schema()).unwrap();
3257 assert_eq!(bi.as_ref(), "99999999999999999999999");
3258 }
3259
3260 #[test]
3261 fn read_big_integer_accepts_json_string() {
3262 let bytes = br#""99999999999999999999999""#;
3267 let mut deser = JsonDeserializer::new(bytes, Arc::new(JsonCodecSettings::default()));
3268 let bi = deser.read_big_integer(dummy_schema()).unwrap();
3269 assert_eq!(bi.as_ref(), "99999999999999999999999");
3270 }
3271
3272 #[test]
3273 fn read_big_integer_rejects_other_types() {
3274 let bytes = b"true";
3276 let mut deser = JsonDeserializer::new(bytes, Arc::new(JsonCodecSettings::default()));
3277 let err = deser.read_big_integer(dummy_schema()).unwrap_err();
3278 assert!(
3279 matches!(err, SerdeError::TypeMismatch { .. }),
3280 "expected TypeMismatch, got {err:?}"
3281 );
3282 }
3283
3284 #[test]
3285 fn read_big_decimal_accepts_json_number() {
3286 let bytes = b"1.234567890123456789012345";
3287 let mut deser = JsonDeserializer::new(bytes, Arc::new(JsonCodecSettings::default()));
3288 let bd = deser.read_big_decimal(dummy_schema()).unwrap();
3289 assert_eq!(bd.as_ref(), "1.234567890123456789012345");
3290 }
3291
3292 #[test]
3293 fn read_big_decimal_accepts_json_string() {
3294 let bytes = br#""1.234567890123456789012345""#;
3295 let mut deser = JsonDeserializer::new(bytes, Arc::new(JsonCodecSettings::default()));
3296 let bd = deser.read_big_decimal(dummy_schema()).unwrap();
3297 assert_eq!(bd.as_ref(), "1.234567890123456789012345");
3298 }
3299
3300 #[test]
3301 fn read_big_decimal_rejects_other_types() {
3302 let bytes = b"[]";
3303 let mut deser = JsonDeserializer::new(bytes, Arc::new(JsonCodecSettings::default()));
3304 let err = deser.read_big_decimal(dummy_schema()).unwrap_err();
3305 assert!(
3306 matches!(err, SerdeError::TypeMismatch { .. }),
3307 "expected TypeMismatch, got {err:?}"
3308 );
3309 }
3310
3311 #[test]
3312 fn read_big_integer_rejects_malformed_string() {
3313 let bytes = br#""not-a-number""#;
3317 let mut deser = JsonDeserializer::new(bytes, Arc::new(JsonCodecSettings::default()));
3318 let err = deser.read_big_integer(dummy_schema()).unwrap_err();
3319 assert!(
3320 matches!(err, SerdeError::InvalidInput { .. }),
3321 "expected InvalidInput, got {err:?}"
3322 );
3323 }
3324
3325 #[test]
3328 fn read_discriminated_document_attaches_settings() {
3329 let mut deser = JsonDeserializer::new(b"\"hello\"", Arc::new(JsonCodecSettings::default()));
3333 let wrapper = deser.read_discriminated_document().unwrap();
3334 assert!(
3335 wrapper.settings().is_some(),
3336 "settings should be attached to the wrapper produced by \
3337 read_discriminated_document"
3338 );
3339 }
3340
3341 #[test]
3342 fn read_discriminated_document_settings_enable_blob_coercion() {
3343 let mut deser =
3348 JsonDeserializer::new(br#""aGVsbG8=""#, Arc::new(JsonCodecSettings::default()));
3349 let wrapper = deser.read_discriminated_document().unwrap();
3350 let bytes = wrapper
3351 .as_blob()
3352 .expect("base64 string should decode to blob");
3353 assert_eq!(&*bytes, b"hello");
3354 }
3355
3356 #[test]
3357 fn read_discriminated_document_settings_enable_timestamp_coercion() {
3358 let mut deser =
3361 JsonDeserializer::new(b"1577836800", Arc::new(JsonCodecSettings::default()));
3362 let wrapper = deser.read_discriminated_document().unwrap();
3363 let ts = wrapper
3364 .as_timestamp()
3365 .expect("epoch-seconds number should decode to timestamp");
3366 assert_eq!(ts.secs(), 1577836800);
3367 }
3368
3369 #[test]
3372 fn read_discriminated_document_lifts_absolute_type_into_discriminator() {
3373 let input = br#"{"__type":"smithy.example#Bird","name":"Iago"}"#;
3377 let mut deser = JsonDeserializer::new(input, Arc::new(JsonCodecSettings::default()));
3378 let wrapper = deser.read_discriminated_document().expect("parse succeeds");
3379
3380 let id = wrapper
3381 .discriminator()
3382 .expect("absolute __type lifted into discriminator");
3383 assert_eq!(id, "smithy.example#Bird");
3384
3385 let map = wrapper.document().as_object().expect("top-level is map");
3386 assert!(
3387 !map.contains_key("__type"),
3388 "__type must be dropped from result map after lift"
3389 );
3390 assert_eq!(map.get("name").and_then(Document::as_string), Some("Iago"));
3391 }
3392
3393 #[test]
3394 fn read_discriminated_document_relative_type_stays_in_map_when_no_default_namespace() {
3395 let input = br#"{"__type":"Bird","name":"Iago"}"#;
3401 let mut deser = JsonDeserializer::new(input, Arc::new(JsonCodecSettings::default()));
3402 let wrapper = deser.read_discriminated_document().expect("parse succeeds");
3403
3404 assert!(
3405 wrapper.discriminator().is_none(),
3406 "relative __type without default_namespace must not be lifted"
3407 );
3408 let map = wrapper.document().as_object().expect("top-level is map");
3409 assert_eq!(
3410 map.get("__type").and_then(Document::as_string),
3411 Some("Bird")
3412 );
3413 }
3414
3415 #[test]
3416 fn read_discriminated_document_lifts_only_top_level() {
3417 let input = br#"{"outer":{"__type":"smithy.example#Inner"}}"#;
3422 let mut deser = JsonDeserializer::new(input, Arc::new(JsonCodecSettings::default()));
3423 let wrapper = deser.read_discriminated_document().expect("parse succeeds");
3424
3425 assert!(wrapper.discriminator().is_none());
3428 let inner = wrapper
3431 .document()
3432 .as_object()
3433 .and_then(|m| m.get("outer"))
3434 .and_then(Document::as_object)
3435 .expect("outer key present and is object");
3436 assert_eq!(
3437 inner.get("__type").and_then(Document::as_string),
3438 Some("smithy.example#Inner"),
3439 "nested __type must remain as a regular map entry: \
3440 only the top-level wrapper has a discriminator slot",
3441 );
3442 }
3443
3444 #[test]
3445 fn read_discriminated_document_malformed_type_stays_in_map() {
3446 for malformed in [
3450 "\"smithy.example#Foo$bar\"", "\"smithy.example#\"", "\"#smithy.example\"", "\"no#hash#twice\"", ] {
3455 let body = format!(r#"{{"__type":{malformed}}}"#);
3456 let mut deser =
3457 JsonDeserializer::new(body.as_bytes(), Arc::new(JsonCodecSettings::default()));
3458 let wrapper = deser.read_discriminated_document().expect("parse succeeds");
3459 assert!(
3460 wrapper.discriminator().is_none(),
3461 "malformed __type {malformed} must not be lifted"
3462 );
3463 let map = wrapper.document().as_object().expect("top-level is map");
3464 assert!(
3465 map.contains_key("__type"),
3466 "malformed __type {malformed} must remain in map"
3467 );
3468 }
3469 }
3470
3471 #[test]
3472 fn read_discriminated_document_non_string_type_stays_in_map() {
3473 for non_string in [
3476 r#"42"#,
3477 r#"true"#,
3478 r#"null"#,
3479 r#"["a"]"#,
3480 r#"{"nested":true}"#,
3481 ] {
3482 let body = format!(r#"{{"__type":{non_string}}}"#);
3483 let mut deser =
3484 JsonDeserializer::new(body.as_bytes(), Arc::new(JsonCodecSettings::default()));
3485 let wrapper = deser.read_discriminated_document().expect("parse succeeds");
3486 assert!(
3487 wrapper.discriminator().is_none(),
3488 "non-string __type {non_string} must not be lifted"
3489 );
3490 let map = wrapper.document().as_object().expect("top-level is map");
3491 assert!(
3492 map.contains_key("__type"),
3493 "non-string __type {non_string} must remain in map"
3494 );
3495 }
3496 }
3497
3498 fn settings_with_default_namespace(ns: &str) -> Arc<JsonCodecSettings> {
3501 Arc::new(JsonCodecSettings::builder().default_namespace(ns).build())
3502 }
3503
3504 #[test]
3505 fn read_discriminated_document_lifts_relative_with_namespace() {
3506 let input = br#"{"__type":"Capacity","CapacityUnits":1.0}"#;
3511 let settings = settings_with_default_namespace("com.amazonaws.dynamodb");
3512 let mut deser = JsonDeserializer::new(input, settings);
3513 let wrapper = deser.read_discriminated_document().expect("parse succeeds");
3514
3515 assert_eq!(
3516 wrapper.discriminator(),
3517 Some("com.amazonaws.dynamodb#Capacity"),
3518 "relative __type must be resolved against default_namespace"
3519 );
3520 let map = wrapper.document().as_object().expect("top-level is map");
3521 assert!(
3522 !map.contains_key("__type"),
3523 "lifted __type must be removed from the map"
3524 );
3525 assert!(
3526 map.contains_key("CapacityUnits"),
3527 "non-discriminator keys remain"
3528 );
3529 }
3530
3531 #[test]
3532 fn read_discriminated_document_absolute_overrides_default_namespace() {
3533 let input = br#"{"__type":"smithy.example#Bird","name":"Iago"}"#;
3537 let settings = settings_with_default_namespace("com.amazonaws.dynamodb");
3538 let mut deser = JsonDeserializer::new(input, settings);
3539 let wrapper = deser.read_discriminated_document().expect("parse succeeds");
3540
3541 assert_eq!(
3542 wrapper.discriminator(),
3543 Some("smithy.example#Bird"),
3544 "absolute __type must override default_namespace"
3545 );
3546 }
3547
3548 #[test]
3549 fn read_discriminated_document_invalid_relative_name_stays_in_map() {
3550 for invalid in ["foo bar", "foo-bar", "foo.bar", "1leading-digit", ""] {
3555 let body = format!(r#"{{"__type":"{invalid}"}}"#);
3556 let settings = settings_with_default_namespace("com.example");
3557 let mut deser = JsonDeserializer::new(body.as_bytes(), settings);
3558 let wrapper = deser.read_discriminated_document().expect("parse succeeds");
3559 assert!(
3560 wrapper.discriminator().is_none(),
3561 "invalid relative name {invalid:?} must not be lifted"
3562 );
3563 let map = wrapper.document().as_object().expect("top-level is map");
3564 assert!(
3565 map.contains_key("__type"),
3566 "invalid relative name {invalid:?} must remain in map"
3567 );
3568 }
3569 }
3570
3571 #[test]
3572 fn read_discriminated_document_accepts_relative_names_with_underscore() {
3573 for valid in ["_Bird", "Bird1", "_a_b_c", "Foo_Bar"] {
3576 let body = format!(r#"{{"__type":"{valid}"}}"#);
3577 let settings = settings_with_default_namespace("ns");
3578 let mut deser = JsonDeserializer::new(body.as_bytes(), settings);
3579 let wrapper = deser.read_discriminated_document().expect("parse succeeds");
3580 assert_eq!(
3581 wrapper.discriminator(),
3582 Some(format!("ns#{valid}").as_str()),
3583 "valid Smithy identifier {valid:?} must be lifted"
3584 );
3585 }
3586 }
3587}
3588
3589#[cfg(test)]
3605mod union_deserialization_tests {
3606 use super::*;
3607 use aws_smithy_schema::{shape_id, ShapeType};
3608
3609 static INNER_LAMBDA: Schema<'static> = Schema::new_member(
3616 shape_id!("test", "InnerUnion"),
3617 ShapeType::String,
3618 "lambda",
3619 0,
3620 );
3621 static INNER_UNION_SCHEMA: Schema<'static> = Schema::new_struct(
3622 shape_id!("test", "InnerUnion"),
3623 ShapeType::Union,
3624 &[&INNER_LAMBDA],
3625 );
3626
3627 static OUTER_MCP: Schema<'static> =
3628 Schema::new_member(shape_id!("test", "OuterUnion"), ShapeType::Union, "mcp", 0);
3629 static OUTER_UNION_SCHEMA: Schema<'static> = Schema::new_struct(
3630 shape_id!("test", "OuterUnion"),
3631 ShapeType::Union,
3632 &[&OUTER_MCP],
3633 );
3634
3635 #[derive(Debug, PartialEq)]
3636 enum InnerUnion {
3637 Lambda(String),
3638 Unknown,
3639 }
3640
3641 #[derive(Debug, PartialEq)]
3642 enum OuterUnion {
3643 Mcp(InnerUnion),
3644 Unknown,
3645 }
3646
3647 fn deserialize_inner(deser: &mut dyn ShapeDeserializer) -> Result<InnerUnion, SerdeError> {
3648 let mut result: Option<InnerUnion> = None;
3649 deser.read_struct(&INNER_UNION_SCHEMA, &mut |member, d| {
3650 result = Some(match member.member_index() {
3651 Some(0) => InnerUnion::Lambda(d.read_string(member)?),
3652 _ => InnerUnion::Unknown,
3653 });
3654 Ok(())
3655 })?;
3656 result.ok_or_else(|| SerdeError::custom("expected a union variant"))
3657 }
3658
3659 fn deserialize_outer(deser: &mut dyn ShapeDeserializer) -> Result<OuterUnion, SerdeError> {
3660 let mut result: Option<OuterUnion> = None;
3661 deser.read_struct(&OUTER_UNION_SCHEMA, &mut |member, d| {
3662 result = Some(match member.member_index() {
3663 Some(0) => OuterUnion::Mcp(deserialize_inner(d)?),
3666 _ => OuterUnion::Unknown,
3667 });
3668 Ok(())
3669 })?;
3670 result.ok_or_else(|| SerdeError::custom("expected a union variant"))
3671 }
3672
3673 static CHOICE_A: Schema<'static> =
3676 Schema::new_member(shape_id!("test", "Choice"), ShapeType::String, "a", 0);
3677 static CHOICE_B: Schema<'static> =
3678 Schema::new_member(shape_id!("test", "Choice"), ShapeType::String, "b", 1);
3679 static CHOICE_SCHEMA: Schema<'static> = Schema::new_struct(
3680 shape_id!("test", "Choice"),
3681 ShapeType::Union,
3682 &[&CHOICE_A, &CHOICE_B],
3683 );
3684
3685 #[derive(Debug, PartialEq)]
3686 enum Choice {
3687 A(String),
3688 B(String),
3689 Unknown,
3690 }
3691
3692 fn deserialize_choice(deser: &mut dyn ShapeDeserializer) -> Result<Choice, SerdeError> {
3693 let mut result: Option<Choice> = None;
3694 deser.read_struct(&CHOICE_SCHEMA, &mut |member, d| {
3695 result = Some(match member.member_index() {
3696 Some(0) => Choice::A(d.read_string(member)?),
3697 Some(1) => Choice::B(d.read_string(member)?),
3698 _ => Choice::Unknown,
3699 });
3700 Ok(())
3701 })?;
3702 result.ok_or_else(|| SerdeError::custom("expected a union variant"))
3703 }
3704
3705 static HOLDER_CHOICE: Schema<'static> =
3709 Schema::new_member(shape_id!("test", "Holder"), ShapeType::Union, "choice", 0);
3710 static HOLDER_TRAILING: Schema<'static> = Schema::new_member(
3711 shape_id!("test", "Holder"),
3712 ShapeType::String,
3713 "trailing",
3714 1,
3715 );
3716 static HOLDER_SCHEMA: Schema<'static> = Schema::new_struct(
3717 shape_id!("test", "Holder"),
3718 ShapeType::Structure,
3719 &[&HOLDER_CHOICE, &HOLDER_TRAILING],
3720 );
3721
3722 fn deser(input: &[u8]) -> JsonDeserializer<'_> {
3723 JsonDeserializer::new(input, Arc::new(JsonCodecSettings::default()))
3724 }
3725
3726 #[test]
3727 fn union_member_targeting_a_union_deserializes_without_panic() {
3728 let mut d = deser(br#"{"mcp":{"lambda":"arn:aws:lambda:fn"}}"#);
3731 let got = deserialize_outer(&mut d).expect("union-in-union must deserialize");
3732 assert_eq!(
3733 got,
3734 OuterUnion::Mcp(InnerUnion::Lambda("arn:aws:lambda:fn".to_string()))
3735 );
3736 }
3737
3738 #[test]
3739 fn explicit_null_first_union_member_resolves_later_variant_without_stream_corruption() {
3740 let mut choice: Option<Choice> = None;
3745 let mut trailing: Option<String> = None;
3746 let mut d = deser(br#"{"choice":{"a":null,"b":"x"},"trailing":"ok"}"#);
3747 d.read_struct(&HOLDER_SCHEMA, &mut |member, sub| {
3748 match member.member_index() {
3749 Some(0) => choice = Some(deserialize_choice(sub)?),
3750 Some(1) => trailing = Some(sub.read_string(member)?),
3751 _ => {}
3752 }
3753 Ok(())
3754 })
3755 .expect("holder with a null-first-member union must parse");
3756 assert_eq!(choice, Some(Choice::B("x".to_string())));
3757 assert_eq!(
3758 trailing,
3759 Some("ok".to_string()),
3760 "trailing sibling must survive: reading the union must not corrupt the stream"
3761 );
3762 }
3763
3764 #[test]
3765 fn empty_union_object_yields_clean_error_not_panic() {
3766 let mut d = deser(br#"{}"#);
3767 let err = deserialize_choice(&mut d).unwrap_err();
3768 assert!(
3769 err.to_string().contains("expected a union variant"),
3770 "empty union object must be a clean error, got {err:?}"
3771 );
3772 }
3773
3774 #[test]
3775 fn all_null_union_members_yield_clean_error_not_panic() {
3776 let mut d = deser(br#"{"a":null,"b":null}"#);
3777 let err = deserialize_choice(&mut d).unwrap_err();
3778 assert!(
3779 err.to_string().contains("expected a union variant"),
3780 "all-null union must be a clean error, got {err:?}"
3781 );
3782 }
3783
3784 #[test]
3785 fn list_of_union_in_union_deserializes() {
3786 let mut d = deser(br#"[{"mcp":{"lambda":"a"}},{"mcp":{"lambda":"b"}}]"#);
3788 let mut out = Vec::new();
3789 d.read_list(&aws_smithy_schema::prelude::STRING, &mut |el| {
3790 out.push(deserialize_outer(el)?);
3791 Ok(())
3792 })
3793 .expect("list of union-in-union must deserialize");
3794 assert_eq!(
3795 out,
3796 vec![
3797 OuterUnion::Mcp(InnerUnion::Lambda("a".to_string())),
3798 OuterUnion::Mcp(InnerUnion::Lambda("b".to_string())),
3799 ]
3800 );
3801 }
3802
3803 #[test]
3804 fn map_of_union_in_union_deserializes() {
3805 let mut d = deser(br#"{"first":{"mcp":{"lambda":"a"}},"second":{"mcp":{"lambda":"b"}}}"#);
3807 let mut out = std::collections::HashMap::new();
3808 d.read_map(&aws_smithy_schema::prelude::STRING, &mut |k, v| {
3809 out.insert(k, deserialize_outer(v)?);
3810 Ok(())
3811 })
3812 .expect("map of union-in-union must deserialize");
3813 assert_eq!(
3814 out.get("first"),
3815 Some(&OuterUnion::Mcp(InnerUnion::Lambda("a".to_string())))
3816 );
3817 assert_eq!(
3818 out.get("second"),
3819 Some(&OuterUnion::Mcp(InnerUnion::Lambda("b".to_string())))
3820 );
3821 }
3822
3823 mod separators {
3824 use super::*;
3830 use aws_smithy_schema::{shape_id, ShapeType};
3831
3832 static INT: Schema<'static> = Schema::new_member(
3833 shape_id!("test", "Input", "int"),
3834 ShapeType::Integer,
3835 "int",
3836 0,
3837 );
3838 static LIST: Schema<'static> = Schema::new_member(
3839 shape_id!("test", "Input", "list"),
3840 ShapeType::List,
3841 "list",
3842 1,
3843 );
3844 static INPUT: Schema<'static> = Schema::new_struct(
3845 shape_id!("test", "Input"),
3846 ShapeType::Structure,
3847 &[&INT, &LIST],
3848 );
3849
3850 fn deser(input: &[u8]) -> JsonDeserializer<'_> {
3851 JsonDeserializer::new(input, Arc::new(JsonCodecSettings::default()))
3852 }
3853
3854 fn read_input(body: &[u8]) -> Result<(Option<i32>, Vec<i32>), SerdeError> {
3856 let mut d = deser(body);
3857 let mut int = None;
3858 let mut list = Vec::new();
3859 d.read_struct(&INPUT, &mut |member, d| {
3860 match member.member_index() {
3861 Some(0) => int = Some(d.read_integer(member)?),
3862 Some(1) => d.read_list(member, &mut |d| {
3863 list.push(d.read_integer(member)?);
3864 Ok(())
3865 })?,
3866 _ => {}
3867 }
3868 Ok(())
3869 })?;
3870 Ok((int, list))
3871 }
3872
3873 #[test]
3874 fn well_formed_input_is_read() {
3875 assert_eq!(
3876 read_input(br#"{ "int": 10 , "list": [ 1 , 2 ] }"#).unwrap(),
3877 (Some(10), vec![1, 2])
3878 );
3879 assert_eq!(read_input(br#"{}"#).unwrap(), (None, vec![]));
3880 assert_eq!(read_input(br#"{"list":[]}"#).unwrap(), (None, vec![]));
3881 assert_eq!(
3882 read_input(b" { \"int\": 1 } \n").unwrap(),
3883 (Some(1), vec![])
3884 );
3885 }
3886
3887 #[test]
3888 fn trailing_comma_in_object_is_rejected() {
3889 assert!(read_input(br#"{"int": 10,}"#).is_err());
3890 }
3891
3892 #[test]
3893 fn leading_and_repeated_commas_in_object_are_rejected() {
3894 assert!(read_input(br#"{,"int": 10}"#).is_err());
3895 assert!(read_input(br#"{"int": 10,,"list": []}"#).is_err());
3896 assert!(read_input(br#"{"int": 10,,}"#).is_err());
3897 }
3898
3899 #[test]
3900 fn missing_comma_in_object_is_rejected() {
3901 assert!(read_input(br#"{"int": 10 "list": []}"#).is_err());
3902 }
3903
3904 #[test]
3905 fn commas_in_array_are_checked() {
3906 assert!(read_input(br#"{"list": [1,]}"#).is_err());
3907 assert!(read_input(br#"{"list": [,1]}"#).is_err());
3908 assert!(read_input(br#"{"list": [1,,2]}"#).is_err());
3909 assert!(read_input(br#"{"list": [1 2]}"#).is_err());
3910 }
3911
3912 #[test]
3913 fn skipped_unknown_members_are_still_validated() {
3914 assert_eq!(
3915 read_input(br#"{"unknown": {"a": [1, {"b": null}], "c": "x"}, "int": 1}"#).unwrap(),
3916 (Some(1), vec![])
3917 );
3918 assert!(read_input(br#"{"unknown": [1,,2], "int": 1}"#).is_err());
3919 assert!(read_input(br#"{"unknown": {"a": 1,}, "int": 1}"#).is_err());
3920 assert!(read_input(br#"{"unknown": "unterminated, "int": 1}"#).is_err());
3921 assert!(read_input(br#"{"unknown": tru, "int": 1}"#).is_err());
3922 }
3923
3924 #[test]
3925 fn trailing_characters_after_the_top_level_value_are_rejected() {
3926 assert!(read_input(br#"{ "int": 10 }abc"#).is_err());
3927 assert!(read_input(br#"{ "int": 10 },"#).is_err());
3928 assert!(read_input(br#"{ "int": 10 }{}"#).is_err());
3929 assert!(deser(br#"[1]x"#).read_integer_list(&LIST).is_err());
3930 assert!(deser(br#"{"k":"v"} {}"#)
3931 .read_string_string_map(&LIST)
3932 .is_err());
3933 assert!(deser(br#"{"a":1}]"#).read_document(&INT).is_err());
3934 assert!(deser(br#"[1,2]"#).read_document(&INT).is_ok());
3935 }
3936
3937 #[test]
3938 fn skipped_numbers_must_match_the_json_grammar() {
3939 for ok in [
3940 b"0" as &[u8],
3941 b"-0",
3942 b"10",
3943 b"-1.5",
3944 b"1e3",
3945 b"1E+3",
3946 b"2.5e-7",
3947 ] {
3948 let body = [b"{\"unknown\": " as &[u8], ok, b", \"int\": 1}"].concat();
3949 assert_eq!(
3950 read_input(&body).unwrap(),
3951 (Some(1), vec![]),
3952 "{}",
3953 String::from_utf8_lossy(ok)
3954 );
3955 }
3956 for bad in [
3957 b"01" as &[u8],
3958 b"1.",
3959 b"--1",
3960 b"+1",
3961 b".5",
3962 b"1e",
3963 b"1e+",
3964 b"-",
3965 b"0x1",
3966 ] {
3967 let body = [b"{\"unknown\": " as &[u8], bad, b", \"int\": 1}"].concat();
3968 assert!(
3969 read_input(&body).is_err(),
3970 "{}",
3971 String::from_utf8_lossy(bad)
3972 );
3973 }
3974 }
3975
3976 #[test]
3977 fn skipped_strings_must_use_valid_escapes() {
3978 assert_eq!(
3979 read_input(br#"{"unknown": "a\"b\\c\/\b\f\n\r\t\u00e9", "int": 1}"#).unwrap(),
3980 (Some(1), vec![])
3981 );
3982 assert!(read_input(br#"{"unknown": "\q", "int": 1}"#).is_err());
3983 assert!(read_input(br#"{"unknown": "\u12", "int": 1}"#).is_err());
3984 assert!(read_input(br#"{"unknown": "\uZZZZ", "int": 1}"#).is_err());
3985 assert!(read_input(br#"{"unknown": "abc\", "int": 1}"#).is_err());
3986 }
3987
3988 #[test]
3989 fn fast_path_collections_check_separators() {
3990 assert_eq!(
3991 deser(br#"["a","b"]"#).read_string_list(&LIST).unwrap(),
3992 vec!["a", "b"]
3993 );
3994 assert!(deser(br#"["a",]"#).read_string_list(&LIST).is_err());
3995 assert!(deser(br#"[,"a"]"#).read_string_list(&LIST).is_err());
3996 assert!(deser(br#"[1,,2]"#).read_integer_list(&LIST).is_err());
3997 assert!(deser(br#"[1,]"#).read_long_list(&LIST).is_err());
3998 assert!(deser(br#"["YQ==",]"#).read_blob_list(&LIST).is_err());
3999 assert_eq!(
4000 deser(br#"{"k":"v"}"#)
4001 .read_string_string_map(&LIST)
4002 .unwrap()
4003 .len(),
4004 1
4005 );
4006 assert!(deser(br#"{"k":"v",}"#)
4007 .read_string_string_map(&LIST)
4008 .is_err());
4009 assert!(deser(br#"{"k":"v" "j":"w"}"#)
4010 .read_string_string_map(&LIST)
4011 .is_err());
4012 }
4013
4014 #[test]
4015 fn generic_map_checks_separators() {
4016 let mut count = 0;
4017 assert!(deser(br#"{"a":1,"b":2}"#)
4018 .read_map(&LIST, &mut |_, d| {
4019 count += 1;
4020 d.read_integer(&INT).map(|_| ())
4021 })
4022 .is_ok());
4023 assert_eq!(count, 2);
4024 assert!(deser(br#"{"a":1,}"#)
4025 .read_map(&LIST, &mut |_, d| d.read_integer(&INT).map(|_| ()))
4026 .is_err());
4027 }
4028
4029 #[test]
4030 fn documents_check_separators() {
4031 assert!(deser(br#"{"a":[1,2],"b":{"c":true}}"#)
4032 .read_document(&INT)
4033 .is_ok());
4034 assert!(deser(br#"{"a":1,}"#).read_document(&INT).is_err());
4035 assert!(deser(br#"[1,,2]"#).read_document(&INT).is_err());
4036 assert!(deser(br#"{,"a":1}"#).read_document(&INT).is_err());
4037 }
4038 }
4039
4040 mod float_strings {
4041 use super::*;
4047 use aws_smithy_schema::{shape_id, ShapeType};
4048
4049 static F: Schema<'static> =
4050 Schema::new_member(shape_id!("test", "Input", "f"), ShapeType::Float, "f", 0);
4051 static D: Schema<'static> =
4052 Schema::new_member(shape_id!("test", "Input", "d"), ShapeType::Double, "d", 1);
4053 static INPUT: Schema<'static> =
4054 Schema::new_struct(shape_id!("test", "Input"), ShapeType::Structure, &[&F, &D]);
4055
4056 fn read(body: &[u8]) -> Result<(Option<f32>, Option<f64>), SerdeError> {
4057 let mut deser = JsonDeserializer::new(body, Arc::new(JsonCodecSettings::default()));
4058 let (mut f, mut d) = (None, None);
4059 deser.read_struct(&INPUT, &mut |m, x| {
4060 match m.member_index() {
4061 Some(0) => f = Some(x.read_float(m)?),
4062 Some(1) => d = Some(x.read_double(m)?),
4063 _ => {}
4064 }
4065 Ok(())
4066 })?;
4067 Ok((f, d))
4068 }
4069
4070 #[test]
4071 fn numbers_and_the_three_special_strings_are_read() {
4072 assert_eq!(
4073 read(br#"{"f": 1.5, "d": -2}"#).unwrap(),
4074 (Some(1.5), Some(-2.0))
4075 );
4076 let (f, d) = read(br#"{"f": "NaN", "d": "-Infinity"}"#).unwrap();
4077 assert!(f.unwrap().is_nan());
4078 assert_eq!(d, Some(f64::NEG_INFINITY));
4079 assert_eq!(
4080 read(br#"{"f": "Infinity"}"#).unwrap().0,
4081 Some(f32::INFINITY)
4082 );
4083 }
4084
4085 #[test]
4086 fn quoted_finite_numbers_are_rejected() {
4087 assert!(read(br#"{"f": "123"}"#).is_err());
4088 assert!(read(br#"{"d": "123"}"#).is_err());
4089 assert!(read(br#"{"d": "1.5e3"}"#).is_err());
4090 assert!(read(br#"{"d": "-0"}"#).is_err());
4091 }
4092
4093 #[test]
4094 fn non_numeric_strings_are_rejected() {
4095 assert!(read(br#"{"d": "abc"}"#).is_err());
4096 assert!(read(br#"{"d": ""}"#).is_err());
4097 assert!(read(br#"{"d": "1 2"}"#).is_err());
4098 }
4099
4100 #[test]
4101 fn alternative_non_finite_spellings_match_the_legacy_parser() {
4102 assert!(read(br#"{"f": "nan"}"#).unwrap().0.unwrap().is_nan());
4105 assert_eq!(read(br#"{"d": "inf"}"#).unwrap().1, Some(f64::INFINITY));
4106 assert_eq!(
4107 read(br#"{"d": "-infinity"}"#).unwrap().1,
4108 Some(f64::NEG_INFINITY)
4109 );
4110 }
4111 }
4112
4113 mod timestamp_formats {
4114 use super::*;
4118 use aws_smithy_schema::traits::TimestampFormat;
4119 use aws_smithy_schema::{shape_id, ShapeType};
4120
4121 static DEFAULT: Schema<'static> =
4122 Schema::new_member(shape_id!("test", "T", "d"), ShapeType::Timestamp, "d", 0);
4123 static DATE_TIME: Schema<'static> =
4124 Schema::new_member(shape_id!("test", "T", "dt"), ShapeType::Timestamp, "dt", 1)
4125 .with_timestamp_format(TimestampFormat::DateTime);
4126 static HTTP_DATE: Schema<'static> =
4127 Schema::new_member(shape_id!("test", "T", "hd"), ShapeType::Timestamp, "hd", 2)
4128 .with_timestamp_format(TimestampFormat::HttpDate);
4129 static EPOCH: Schema<'static> =
4130 Schema::new_member(shape_id!("test", "T", "es"), ShapeType::Timestamp, "es", 3)
4131 .with_timestamp_format(TimestampFormat::EpochSeconds);
4132
4133 const T: i64 = 1515531081;
4135
4136 fn read(strict: bool, schema: &Schema<'_>, input: &[u8]) -> Result<DateTime, SerdeError> {
4137 let settings = JsonCodecSettings::builder()
4138 .strict_timestamp_formats(strict)
4139 .build();
4140 JsonDeserializer::new(input, Arc::new(settings)).read_timestamp(schema)
4141 }
4142
4143 fn assert_rejected(schema: &Schema<'_>, inputs: &[&[u8]]) {
4144 for input in inputs {
4145 assert!(
4146 read(true, schema, input).is_err(),
4147 "{} must be rejected",
4148 String::from_utf8_lossy(input)
4149 );
4150 }
4151 }
4152
4153 #[test]
4154 fn strict_epoch_seconds_default_requires_a_number() {
4155 assert_eq!(
4156 read(true, &DEFAULT, b"1515531081").unwrap(),
4157 DateTime::from_secs(T)
4158 );
4159 assert_eq!(
4160 read(true, &DEFAULT, b"1515531081.1234").unwrap(),
4161 DateTime::from_secs_f64(1515531081.1234)
4162 );
4163 assert_rejected(
4164 &DEFAULT,
4165 &[
4166 br#""1985-04-12T23:20:50.52Z""#,
4167 br#""1985-04-12T23:20:50Z""#,
4168 br#""1996-12-19T16:39:57-08:00""#,
4169 br#""1515531081""#,
4170 br#""1515531081.1234""#,
4171 br#""Tue, 29 Apr 2014 18:30:38 GMT""#,
4172 br#""Infinity""#,
4173 br#""NaN""#,
4174 b"true",
4175 ],
4176 );
4177 }
4178
4179 #[test]
4180 fn strict_explicit_epoch_seconds_requires_a_number() {
4181 assert_eq!(
4182 read(true, &EPOCH, b"1515531081").unwrap(),
4183 DateTime::from_secs(T)
4184 );
4185 assert_rejected(&EPOCH, &[br#""2018-01-09T20:51:21Z""#, br#""1515531081""#]);
4186 }
4187
4188 #[test]
4189 fn strict_date_time_requires_an_rfc3339_utc_string() {
4190 assert_eq!(
4191 read(true, &DATE_TIME, br#""2018-01-09T20:51:21Z""#).unwrap(),
4192 DateTime::from_secs(T)
4193 );
4194 assert_rejected(
4195 &DATE_TIME,
4196 &[
4197 b"1515531081",
4198 b"1515531081.1234",
4199 br#""1996-12-19T16:39:57-08:00""#,
4200 br#""1996-12-19T16:39:57+00""#,
4201 br#""1996-12-19T16:39:57""#,
4202 br#""Tue, 29 Apr 2014 18:30:38 GMT""#,
4203 ],
4204 );
4205 }
4206
4207 #[test]
4208 fn strict_http_date_requires_an_imf_fixdate_string() {
4209 assert_eq!(
4210 read(true, &HTTP_DATE, br#""Tue, 09 Jan 2018 20:51:21 GMT""#).unwrap(),
4211 DateTime::from_secs(T)
4212 );
4213 assert_rejected(
4214 &HTTP_DATE,
4215 &[
4216 b"1515531081",
4217 b"1515531081.1234",
4218 br#""1985-04-12T23:20:50.52Z""#,
4219 br#""1996-12-19T16:39:57-08:00""#,
4220 ],
4221 );
4222 }
4223
4224 #[test]
4225 fn default_is_lenient() {
4226 assert_eq!(
4229 read(false, &DATE_TIME, b"1515531081").unwrap(),
4230 DateTime::from_secs(T)
4231 );
4232 assert_eq!(
4233 read(false, &HTTP_DATE, b"1515531081").unwrap(),
4234 DateTime::from_secs(T)
4235 );
4236 assert_eq!(
4237 read(false, &DEFAULT, br#""2018-01-09T20:51:21Z""#).unwrap(),
4238 DateTime::from_secs(T)
4239 );
4240 assert_eq!(
4241 read(false, &DATE_TIME, br#""2018-01-09T21:51:21+01:00""#).unwrap(),
4242 DateTime::from_secs(T)
4243 );
4244 assert_eq!(
4245 read(false, &HTTP_DATE, br#""Tue, 09 Jan 2018 20:51:21 GMT""#).unwrap(),
4246 DateTime::from_secs(T)
4247 );
4248 assert_eq!(
4249 read(false, &EPOCH, br#""1515531081""#).unwrap(),
4250 DateTime::from_secs(T)
4251 );
4252 }
4253 }
4254}
4255
4256#[cfg(test)]
4261mod unknown_member_tests {
4262 use super::*;
4263 use aws_smithy_schema::{shape_id, ShapeType};
4264
4265 static A: Schema<'static> =
4266 Schema::new_member(shape_id!("test", "S"), ShapeType::String, "a", 0);
4267 static S: Schema<'static> =
4268 Schema::new_struct(shape_id!("test", "S"), ShapeType::Structure, &[&A]);
4269
4270 static U_INT: Schema<'static> =
4271 Schema::new_member(shape_id!("test", "U"), ShapeType::Integer, "int", 0);
4272 static U_STR: Schema<'static> =
4273 Schema::new_member(shape_id!("test", "U"), ShapeType::String, "string", 1);
4274 static U: Schema<'static> =
4275 Schema::new_struct(shape_id!("test", "U"), ShapeType::Union, &[&U_INT, &U_STR]);
4276
4277 fn deser(input: &[u8]) -> JsonDeserializer<'_> {
4278 JsonDeserializer::new(input, Arc::new(JsonCodecSettings::default()))
4279 }
4280
4281 #[test]
4282 fn skipped_string_encoding_and_escape_validation_are_independent() {
4283 let settings = Arc::new(
4284 JsonCodecSettings::builder()
4285 .enforce_strictness(true)
4286 .validate_skipped_values(false)
4287 .validate_skipped_string_encoding(true)
4288 .build(),
4289 );
4290 for (value, valid) in [
4291 (br#""\i""#.as_slice(), true),
4292 (br#""\udee9""#, true),
4293 (b"\"raw\ncontrol\"", false),
4294 (b"\"\xff\"", false),
4295 ] {
4296 let input = [b"{\"unknown\":".as_slice(), value, b",\"a\":\"ok\"}"].concat();
4297 let result = JsonDeserializer::new(&input, settings.clone()).read_struct(
4298 &S,
4299 &mut |member, d| {
4300 if member.member_index().is_some() {
4301 d.read_string(member).map(|_| ())
4302 } else {
4303 Ok(())
4304 }
4305 },
4306 );
4307 assert_eq!(result.is_ok(), valid, "{value:?}");
4308 }
4309 }
4310
4311 #[test]
4312 fn strictness_applies_to_read_values_not_skipped_values() {
4313 let settings = Arc::new(
4314 JsonCodecSettings::builder()
4315 .enforce_strictness(true)
4316 .validate_skipped_values(false)
4317 .build(),
4318 );
4319 for value in [
4320 br#""\udee9""#.as_slice(),
4321 br#""\i""#,
4322 br#""\u12""#,
4323 b"\"raw\ncontrol\"",
4324 b"\"\xff\"",
4325 ] {
4326 for (prefix, suffix) in [
4329 (b"".as_slice(), b"".as_slice()),
4330 (b"[", b"]"),
4331 (b"{\"nested\":", b"}"),
4332 (b"{", b":null}"),
4333 ] {
4334 let input = [
4335 b"{\"unknown\":".as_slice(),
4336 prefix,
4337 value,
4338 suffix,
4339 b",\"a\":\"ok\"}",
4340 ]
4341 .concat();
4342 for explicit_skip in [false, true] {
4343 let mut a = None;
4344 JsonDeserializer::new(&input, settings.clone())
4345 .read_struct(&S, &mut |member, d| {
4346 if member.member_index() == Some(0) {
4347 a = Some(d.read_string(member)?);
4348 } else if explicit_skip {
4349 d.skip_value()?;
4350 }
4351 Ok(())
4352 })
4353 .unwrap();
4354 assert_eq!(a.as_deref(), Some("ok"));
4355 let validating =
4356 Arc::new(settings.to_builder().validate_skipped_values(true).build());
4357 let rejected = JsonDeserializer::new(&input, validating)
4358 .read_struct(&S, &mut |member, d| {
4359 if member.member_index() == Some(0) {
4360 d.read_string(member)?;
4361 } else if explicit_skip {
4362 d.skip_value()?;
4363 }
4364 Ok(())
4365 })
4366 .is_err();
4367 assert_eq!(rejected, value == br#""\i""# || value == br#""\u12""#);
4368 }
4369 }
4370 let input = [b"{\"unknown\":[],\"a\":".as_slice(), value, b"}"].concat();
4373 assert!(JsonDeserializer::new(&input, settings.clone())
4374 .read_struct(&S, &mut |member, d| {
4375 if member.member_index() == Some(0) {
4376 d.read_string(member)?;
4377 }
4378 Ok(())
4379 })
4380 .is_err());
4381 }
4382 }
4383
4384 #[test]
4385 fn skipping_without_content_validation_retains_boundaries_and_limits() {
4386 let settings = Arc::new(
4387 JsonCodecSettings::builder()
4388 .enforce_strictness(true)
4389 .validate_skipped_values(false)
4390 .max_depth(3)
4391 .build(),
4392 );
4393 for input in [
4394 br#"{"unknown":"unterminated}"#.as_slice(),
4395 br#"{"unknown":"escaped\"}"#,
4396 br#"{"unknown":[1,2}"#,
4397 br#"{"unknown":[1,,2]}"#,
4398 br#"{"unknown":01}"#,
4399 br#"{"unknown":tru}"#,
4400 br#"{"unknown":[[[[]]]]}"#,
4401 ] {
4402 assert!(
4403 JsonDeserializer::new(input, settings.clone())
4404 .read_struct(&S, &mut |_, _| Ok(()))
4405 .is_err(),
4406 "{input:?}"
4407 );
4408 }
4409 let input = br#"{"unknown":"\i\"still ignored\\","a":"ok"}"#;
4410 let mut a = None;
4411 JsonDeserializer::new(input, settings)
4412 .read_struct(&S, &mut |member, d| {
4413 if member.member_index() == Some(0) {
4414 a = Some(d.read_string(member)?);
4415 }
4416 Ok(())
4417 })
4418 .unwrap();
4419 assert_eq!(a.as_deref(), Some("ok"));
4420 }
4421
4422 fn read_s(input: &[u8], read_unknown_value: bool) -> (Option<String>, Vec<Option<Document>>) {
4424 let mut a = None;
4425 let mut unknown = Vec::new();
4426 deser(input)
4427 .read_struct(&S, &mut |member, d| {
4428 match member.member_index() {
4429 Some(0) => a = Some(d.read_string(member)?),
4430 _ => {
4431 assert_eq!(member.shape_type(), ShapeType::Document);
4432 unknown.push(if read_unknown_value {
4433 Some(d.read_document(member)?)
4434 } else {
4435 None
4436 });
4437 }
4438 }
4439 Ok(())
4440 })
4441 .unwrap();
4442 (a, unknown)
4443 }
4444
4445 #[test]
4446 fn unknown_key_is_reported_and_the_consumer_can_read_the_value() {
4447 let (a, unknown) = read_s(br#"{"zzz":{"deep":[1,2]},"a":"x"}"#, true);
4448 assert_eq!(a.as_deref(), Some("x"));
4449 assert_eq!(unknown.len(), 1);
4450 match &unknown[0] {
4451 Some(Document::Object(map)) => assert!(map.contains_key("deep")),
4452 other => panic!("expected the object value, got {other:?}"),
4453 }
4454 }
4455
4456 #[test]
4457 fn unknown_key_value_is_skipped_when_the_consumer_does_not_read_it() {
4458 let (a, unknown) = read_s(
4460 br#"{"zzz":{"deep":[1,{"q":"}"}]},"a":"x","yyy":"tail"}"#,
4461 false,
4462 );
4463 assert_eq!(a.as_deref(), Some("x"));
4464 assert_eq!(unknown.len(), 2);
4465 }
4466
4467 #[test]
4468 fn type_discriminator_is_not_reported() {
4469 let (a, unknown) = read_s(br#"{"__type":"ns#Foo","a":"x"}"#, false);
4470 assert_eq!(a.as_deref(), Some("x"));
4471 assert!(unknown.is_empty(), "`__type` must not be reported");
4472 }
4473
4474 #[test]
4475 fn null_valued_unknown_key_is_not_reported() {
4476 let (a, unknown) = read_s(br#"{"zzz":null,"a":"x"}"#, false);
4477 assert_eq!(a.as_deref(), Some("x"));
4478 assert!(unknown.is_empty(), "a null value is an absent member");
4479 }
4480
4481 #[test]
4482 fn struct_consumer_that_ignores_unknown_keys_is_unaffected() {
4483 let mut a = None;
4484 deser(br#"{"zzz":[1,2,3],"a":"x"}"#)
4485 .read_struct(&S, &mut |member, d| {
4486 if member.member_index() == Some(0) {
4487 a = Some(d.read_string(member)?);
4488 }
4489 Ok(())
4490 })
4491 .unwrap();
4492 assert_eq!(a.as_deref(), Some("x"));
4493 }
4494
4495 fn read_union(input: &[u8]) -> Result<String, SerdeError> {
4498 let mut result: Option<String> = None;
4499 deser(input).read_struct(&U, &mut |member, d| {
4500 if result.is_some() {
4501 return Err(SerdeError::invalid_input(
4502 "encountered mixed variants in union",
4503 ));
4504 }
4505 result = Some(match member.member_index() {
4506 Some(0) => format!("int={}", d.read_integer(member)?),
4507 Some(1) => format!("string={}", d.read_string(member)?),
4508 _ => return Err(SerdeError::invalid_input("unexpected union variant")),
4509 });
4510 Ok(())
4511 })?;
4512 result.ok_or_else(|| SerdeError::custom("expected a union variant"))
4513 }
4514
4515 #[test]
4516 fn union_consumer_sees_known_and_unknown_keys() {
4517 assert_eq!(read_union(br#"{"int":2}"#).unwrap(), "int=2");
4518 let err = read_union(br#"{"int":2,"string":"three"}"#).unwrap_err();
4519 assert!(err.to_string().contains("mixed variants"), "{err}");
4520 let err = read_union(br#"{"int":2,"unknownField":"three"}"#).unwrap_err();
4521 assert!(err.to_string().contains("mixed variants"), "{err}");
4522 let err = read_union(br#"{"unknownField":"three"}"#).unwrap_err();
4523 assert!(
4524 err.to_string().contains("unexpected union variant"),
4525 "{err}"
4526 );
4527 assert_eq!(
4529 read_union(br#"{"__type":"ns#U","int":2}"#).unwrap(),
4530 "int=2"
4531 );
4532 }
4533}
4534
4535#[cfg(test)]
4542mod skip_value_contract {
4543 use super::*;
4544 use aws_smithy_schema::Schema;
4545
4546 static BOUND: Schema<'static> = Schema::new_member(
4547 aws_smithy_schema::shape_id!("test", "Output"),
4548 aws_smithy_schema::ShapeType::String,
4549 "bound",
4550 0,
4551 );
4552 static BODY: Schema<'static> = Schema::new_member(
4553 aws_smithy_schema::shape_id!("test", "Output"),
4554 aws_smithy_schema::ShapeType::String,
4555 "body",
4556 1,
4557 );
4558 static OUTPUT: Schema<'static> = Schema::new_struct(
4559 aws_smithy_schema::shape_id!("test", "Output"),
4560 aws_smithy_schema::ShapeType::Structure,
4561 &[&BOUND, &BODY],
4562 );
4563
4564 fn skip_bound_read_body(json: &[u8]) -> Result<Option<String>, SerdeError> {
4568 let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
4569 let mut body = None;
4570 deser.read_struct(&OUTPUT, &mut |member, d| {
4571 match member.member_index() {
4572 Some(0) => d.skip_value()?,
4573 Some(1) => body = Some(d.read_string(member)?),
4574 _ => {}
4575 }
4576 Ok(())
4577 })?;
4578 Ok(body)
4579 }
4580
4581 #[test]
4582 fn skipping_a_known_member_lets_the_rest_of_the_struct_parse() {
4583 for value in [
4586 r#""a string""#,
4587 r#""a string with a \" escape and , comma""#,
4588 "123",
4589 "-4.5e10",
4590 "true",
4591 "false",
4592 "null",
4593 "{}",
4594 "[]",
4595 r#"{"nested":{"deeper":[1,2,{"x":"y"}]}}"#,
4596 r#"[[[["deep"]]]]"#,
4597 r#"[{"a":1},{"b":[true,null]}]"#,
4598 r#"{"body":"decoy"}"#,
4601 ] {
4602 let json = format!(r#"{{"bound":{value},"body":"real"}}"#);
4603 let got = skip_bound_read_body(json.as_bytes())
4604 .unwrap_or_else(|e| panic!("skipping {value} failed: {e}"));
4605 assert_eq!(
4606 got.as_deref(),
4607 Some("real"),
4608 "after skipping {value}, `body` should still be read"
4609 );
4610 }
4611 }
4612
4613 #[test]
4614 fn skipping_the_last_member_closes_the_object() {
4615 let json = br#"{"body":"real","bound":{"nested":[1,2]}}"#;
4616 assert_eq!(skip_bound_read_body(json).unwrap().as_deref(), Some("real"));
4617 }
4618
4619 #[test]
4620 fn a_skipped_value_is_still_validated() {
4621 for bad in [
4624 r#"{"bound":{"unclosed":1,"body":"real"}"#,
4625 r#"{"bound":01,"body":"real"}"#,
4626 r#"{"bound":tru,"body":"real"}"#,
4627 r#"{"bound":"unterminated,"body":"real"}"#,
4628 ] {
4629 assert!(
4630 skip_bound_read_body(bad.as_bytes()).is_err(),
4631 "malformed skipped value should be rejected: {bad}"
4632 );
4633 }
4634 }
4635
4636 #[test]
4637 fn skip_value_via_trait_object_matches_the_inherent_skip() {
4638 let json = br#"{"a":{"b":[1,2,3]},"tail":1}"#;
4640 let settings = Arc::new(JsonCodecSettings::default());
4641
4642 let mut inherent = JsonDeserializer::new(json, settings.clone());
4643 inherent.advance_by(5); inherent.skip_json_value().unwrap();
4645 let inherent_pos = inherent.position;
4646
4647 let mut via_trait = JsonDeserializer::new(json, settings);
4648 via_trait.advance_by(5);
4649 let dynamic: &mut dyn ShapeDeserializer = &mut via_trait;
4650 dynamic.skip_value().unwrap();
4651 assert_eq!(via_trait.position, inherent_pos);
4652 }
4653
4654 #[test]
4655 fn skip_value_on_an_unknown_member_does_not_double_skip() {
4656 let json = br#"{"unknown":{"x":1},"body":"real"}"#;
4660 let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
4661 let mut body = None;
4662 let mut saw_unknown = false;
4663 deser
4664 .read_struct(&OUTPUT, &mut |member, d| {
4665 match member.member_index() {
4666 Some(1) => body = Some(d.read_string(member)?),
4667 None => {
4670 saw_unknown = true;
4671 d.skip_value()?;
4672 }
4673 _ => {}
4674 }
4675 Ok(())
4676 })
4677 .unwrap();
4678 assert!(saw_unknown, "unknown key should still be reported");
4679 assert_eq!(
4680 body.as_deref(),
4681 Some("real"),
4682 "`body` must not be swallowed by a second skip"
4683 );
4684 }
4685
4686 #[test]
4687 fn unknown_member_is_still_reported_and_skipped_when_the_consumer_ignores_it() {
4688 let json = br#"{"unknown":{"x":[1,2]},"body":"real"}"#;
4690 let mut deser = JsonDeserializer::new(json, Arc::new(JsonCodecSettings::default()));
4691 let mut body = None;
4692 deser
4693 .read_struct(&OUTPUT, &mut |member, d| {
4694 if member.member_index() == Some(1) {
4695 body = Some(d.read_string(member)?);
4696 }
4697 Ok(())
4698 })
4699 .unwrap();
4700 assert_eq!(body.as_deref(), Some("real"));
4701 }
4702}