1use super::XmlCodecSettings;
9use crate::decode::{self, Document};
10use aws_smithy_schema::serde::{SerdeError, ShapeDeserializer};
11use aws_smithy_schema::Schema;
12use aws_smithy_types::date_time::Format as TimestampFormat;
13use aws_smithy_types::{BigDecimal, BigInteger, Blob, DateTime};
14
15use aws_smithy_types::Document as SmithyDocument;
16use std::borrow::Cow;
17use std::sync::Arc;
18
19pub(crate) const MAX_DESERIALIZE_DEPTH: u32 = 128;
23
24pub struct XmlDeserializer<'a> {
41 input: &'a [u8],
45 text: Option<Cow<'a, str>>,
48 settings: Arc<XmlCodecSettings>,
49 depth: u32,
55}
56
57impl<'a> XmlDeserializer<'a> {
58 pub(crate) fn new(input: &'a [u8], settings: Arc<XmlCodecSettings>) -> Self {
60 Self {
61 input,
62 text: None,
63 settings,
64 depth: 0,
65 }
66 }
67
68 #[cfg(test)]
73 fn from_text(text: Cow<'a, str>, settings: Arc<XmlCodecSettings>) -> Self {
74 Self {
75 input: b"",
76 text: Some(text),
77 settings,
78 depth: 0,
79 }
80 }
81
82 fn enter_aggregate(&mut self) -> Result<(), SerdeError> {
92 if self.depth >= self.settings.max_depth() {
93 return Err(SerdeError::custom("maximum nesting depth exceeded"));
94 }
95 self.depth += 1;
96 Ok(())
97 }
98
99 fn leave_aggregate(&mut self) {
102 debug_assert!(self.depth > 0, "leave_aggregate without enter_aggregate");
103 self.depth = self.depth.saturating_sub(1);
104 }
105
106 fn document(&self) -> Result<Document<'a>, SerdeError> {
110 if self.text.is_some() {
111 return Err(SerdeError::custom("expected XML element, found text"));
112 }
113 Ok(Document::try_from(self.input).unwrap_or_else(|_| Document::new("")))
114 }
115
116 fn take_text(&mut self) -> Result<Cow<'a, str>, SerdeError> {
120 if let Some(t) = self.text.take() {
121 return Ok(t);
122 }
123 let mut doc = Document::try_from(self.input).unwrap_or_else(|_| Document::new(""));
124 let mut root = doc
125 .root_element()
126 .map_err(|e| SerdeError::custom(e.to_string()))?;
127 decode::try_data(&mut root).map_err(|e| SerdeError::custom(e.to_string()))
128 }
129
130 fn dispatch_subslice<R>(&mut self, sub: &'a [u8], f: impl FnOnce(&mut Self) -> R) -> R {
135 let saved_input = std::mem::replace(&mut self.input, sub);
136 let saved_text = self.text.take();
137 let r = f(self);
138 self.input = saved_input;
139 self.text = saved_text;
140 r
141 }
142
143 fn dispatch_text<R>(&mut self, text: Cow<'a, str>, f: impl FnOnce(&mut Self) -> R) -> R {
146 let saved_input = std::mem::replace(&mut self.input, b"");
147 let saved_text = self.text.replace(text);
148 let r = f(self);
149 self.input = saved_input;
150 self.text = saved_text;
151 r
152 }
153
154 fn resolve_member<'s>(schema: &'s Schema<'s>, element_name: &str) -> Option<&'s Schema<'s>> {
158 schema.members().iter().copied().find(|m| {
159 if let Some(xml_name) = m.xml_name() {
160 xml_name.value() == element_name
161 } else {
162 m.member_name() == Some(element_name)
163 }
164 })
165 }
166
167 pub(crate) fn find_element_slice(input: &'a [u8], el_local: &str) -> &'a [u8] {
181 debug_assert!(
189 {
190 let lo = input.as_ptr() as usize;
191 let hi = lo + input.len();
192 let p = el_local.as_ptr() as usize;
193 p >= lo && p + el_local.len() <= hi
194 },
195 "find_element_slice: el_local must point into input"
196 );
197 let name_ptr = el_local.as_ptr() as usize;
198 let input_start = input.as_ptr() as usize;
199 let name_offset = name_ptr.saturating_sub(input_start).min(input.len());
200
201 let el_start = input[..name_offset]
204 .iter()
205 .rposition(|&b| b == b'<')
206 .unwrap_or(0);
207
208 let tag_name = el_local.as_bytes();
212 let remaining = &input[el_start..];
213 let mut depth = 0i32;
214 let mut pos = 0;
215 while pos < remaining.len() {
216 if remaining[pos..].starts_with(b"</") {
217 let after_slash = pos + 2;
219 if remaining[after_slash..].starts_with(tag_name) {
220 let after_name = after_slash + tag_name.len();
221 if remaining.get(after_name) == Some(&b'>') {
222 depth -= 1;
223 if depth == 0 {
224 let end = el_start + after_name + 1;
225 return &input[el_start..end];
226 }
227 }
228 }
229 pos = after_slash;
230 } else if remaining[pos] == b'<'
231 && remaining.get(pos + 1) != Some(&b'/')
232 && remaining.get(pos + 1) != Some(&b'?')
233 {
234 if let Some(gt) = remaining[pos..].iter().position(|&b| b == b'>') {
236 let tag_content = &remaining[pos + 1..pos + gt];
237 let is_self_closing = tag_content.last() == Some(&b'/');
238 let opens_our_tag = tag_content.starts_with(tag_name)
239 && tag_content
240 .get(tag_name.len())
241 .is_none_or(|&b| b == b' ' || b == b'>' || b == b'/');
242 if opens_our_tag && is_self_closing && depth == 0 {
243 let end = el_start + pos + gt + 1;
247 return &input[el_start..end];
248 }
249 if opens_our_tag && !is_self_closing {
250 depth += 1;
251 }
252 pos += gt + 1;
253 } else {
254 pos += 1;
255 }
256 } else {
257 pos += 1;
263 }
264 }
265 &input[el_start..]
267 }
268
269 fn resolve_timestamp_format(&self, schema: &Schema<'_>) -> Result<TimestampFormat, SerdeError> {
270 let Some(t) = schema.timestamp_format() else {
271 return Ok(match self.settings.default_timestamp_format() {
272 TimestampFormat::DateTime => TimestampFormat::DateTimeWithOffset,
273 other => other,
274 });
275 };
276 Ok(match t.format() {
277 aws_smithy_schema::traits::TimestampFormat::EpochSeconds => {
278 TimestampFormat::EpochSeconds
279 }
280 aws_smithy_schema::traits::TimestampFormat::DateTime => {
285 TimestampFormat::DateTimeWithOffset
286 }
287 aws_smithy_schema::traits::TimestampFormat::HttpDate => TimestampFormat::HttpDate,
288 other => {
289 return Err(SerdeError::unsupported(format!(
290 "unsupported timestamp format {other:?}"
291 )))
292 }
293 })
294 }
295}
296
297pub fn find_depth2_element_slice_by(
311 body: &[u8],
312 predicate: impl Fn(&str) -> bool,
313) -> Option<&[u8]> {
314 let mut doc = Document::try_from(body).ok()?;
315 let mut root = doc.root_element().ok()?;
316 if predicate(root.start_el().local()) {
319 return Some(body);
320 }
321 while let Some(tag) = root.next_tag() {
323 let local = tag.start_el().local();
324 if predicate(local) {
325 return Some(XmlDeserializer::find_element_slice(body, local));
328 }
329 }
330 None
331}
332
333impl ShapeDeserializer for XmlDeserializer<'_> {
334 fn read_struct(
335 &mut self,
336 schema: &Schema<'_>,
337 consumer: &mut dyn FnMut(&Schema<'_>, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
338 ) -> Result<(), SerdeError> {
339 self.enter_aggregate()?;
340 let result = (|| -> Result<(), SerdeError> {
346 let input = self.input;
352 let mut doc = self.document()?;
353 let mut root = doc
354 .root_element()
355 .map_err(|e| SerdeError::custom(e.to_string()))?;
356
357 if self.settings.enforce_strictness && self.depth == 1 {
358 let expected = schema
359 .xml_name()
360 .map(|t| t.value())
361 .or_else(|| schema.original_name())
362 .or_else(|| schema.member_name())
363 .unwrap_or_else(|| schema.shape_id().shape_name());
364 if !root.start_el().matches(expected) {
365 return Err(SerdeError::invalid_input(format!(
366 "expected XML root {expected}"
367 )));
368 }
369 }
370
371 if schema.xml_unwrapped_output() {
380 let el_local = root.start_el().local();
392 let sub = Self::find_element_slice(input, el_local);
393 let local = el_local.to_owned();
394 drop(root);
400 let _ = doc;
401 if let Some(member) = Self::resolve_member(schema, &local) {
402 self.dispatch_subslice(sub, |this| consumer(member, this))?;
403 }
404 return Ok(());
405 }
406
407 for member in schema.members() {
409 if member.xml_attribute() {
410 let attr_name = member
411 .xml_name()
412 .map(|t| t.value())
413 .or(member.member_name())
414 .unwrap_or("");
415 if let Some(value) = root.start_el().attr(attr_name) {
416 let text = Cow::Owned(value.to_owned());
417 self.dispatch_text(text, |this| consumer(member, this))?;
418 }
419 }
420 }
421
422 let mut flattened_groups: std::collections::HashMap<usize, (&Schema<'_>, Vec<u8>)> =
426 std::collections::HashMap::new();
427
428 while let Some(mut child_scope) = root.next_tag() {
430 let Some(member) = Self::resolve_member(schema, child_scope.start_el().local())
431 else {
432 continue;
433 };
434 let is_aggregate = member.shape_type().is_aggregate();
442 if is_aggregate && !member.xml_flattened() {
443 let el_local = child_scope.start_el().local();
444 let sub = Self::find_element_slice(input, el_local);
445 drop(child_scope);
446 self.dispatch_subslice(sub, |this| consumer(member, this))?;
447 } else if is_aggregate {
448 let el_local = child_scope.start_el().local();
451 let sub = Self::find_element_slice(input, el_local);
452 drop(child_scope);
453 let idx = member.member_index().unwrap_or(usize::MAX);
454 let entry = flattened_groups
455 .entry(idx)
456 .or_insert_with(|| (member, Vec::new()));
457 entry.1.extend_from_slice(sub);
458 } else {
459 let text = decode::try_data(&mut child_scope)
460 .map_err(|e| SerdeError::custom(e.to_string()))?;
461 drop(child_scope);
462 self.dispatch_text(text, |this| consumer(member, this))?;
463 }
464 }
465
466 for (_idx, (member, bytes)) in flattened_groups {
474 let mut wrapped = Vec::with_capacity(bytes.len() + 16);
475 wrapped.extend_from_slice(b"<__flat>");
476 wrapped.extend_from_slice(&bytes);
477 wrapped.extend_from_slice(b"</__flat>");
478 let mut child_deser = XmlDeserializer::new(&wrapped, self.settings.clone());
479 consumer(member, &mut child_deser)?;
480 }
481 Ok(())
482 })();
483 self.leave_aggregate();
484 result
485 }
486
487 fn read_list(
488 &mut self,
489 _schema: &Schema<'_>,
490 consumer: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
491 ) -> Result<(), SerdeError> {
492 self.enter_aggregate()?;
493 let result = (|| -> Result<(), SerdeError> {
496 let input = self.input;
497 let mut doc = self.document()?;
498 let mut root = doc
499 .root_element()
500 .map_err(|e| SerdeError::custom(e.to_string()))?;
501
502 while let Some(child_scope) = root.next_tag() {
511 let el_local = child_scope.start_el().local();
512 let sub = Self::find_element_slice(input, el_local);
513 drop(child_scope);
514 self.dispatch_subslice(sub, |this| consumer(this))?;
515 }
516 Ok(())
517 })();
518 self.leave_aggregate();
519 result
520 }
521
522 fn read_map(
523 &mut self,
524 schema: &Schema<'_>,
525 consumer: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
526 ) -> Result<(), SerdeError> {
527 self.enter_aggregate()?;
528 let result = (|| -> Result<(), SerdeError> {
531 let input = self.input;
532 let mut doc = self.document()?;
533 let mut root = doc
534 .root_element()
535 .map_err(|e| SerdeError::custom(e.to_string()))?;
536
537 let key_name = schema
539 .key()
540 .and_then(|k| k.xml_name().map(|t| t.value()))
541 .unwrap_or("key");
542 let value_name = schema
543 .member()
544 .and_then(|v| v.xml_name().map(|t| t.value()))
545 .unwrap_or("value");
546
547 let value_is_aggregate = schema
550 .member()
551 .map(|v| v.shape_type().is_aggregate())
552 .unwrap_or(false);
553
554 while let Some(mut entry_scope) = root.next_tag() {
556 let mut key: Option<String> = None;
557 let mut value_text: Option<Cow<'_, str>> = None;
560 let mut value_slice: Option<&'_ [u8]> = None;
561 while let Some(mut field_scope) = entry_scope.next_tag() {
562 let local = field_scope.start_el().local();
563 let (is_key, is_value) = (local == key_name, local == value_name);
564 if is_key {
565 let text = decode::try_data(&mut field_scope)
566 .map_err(|e| SerdeError::custom(e.to_string()))?;
567 key = Some(text.into_owned());
568 } else if is_value {
569 if value_is_aggregate {
570 let el_local = field_scope.start_el().local();
571 let sub = Self::find_element_slice(input, el_local);
572 drop(field_scope);
573 value_slice = Some(sub);
574 } else {
575 let text = decode::try_data(&mut field_scope)
576 .map_err(|e| SerdeError::custom(e.to_string()))?;
577 value_text = Some(text);
578 }
579 }
580 }
581 drop(entry_scope);
582 if let Some(k) = key {
583 if let Some(slice) = value_slice {
584 self.dispatch_subslice(slice, |this| consumer(k, this))?;
585 } else if let Some(t) = value_text {
586 let t: Cow<'_, str> = t;
590 self.dispatch_text(t.into_owned().into(), |this| consumer(k, this))?;
591 }
592 }
593 }
594 Ok(())
595 })();
596 self.leave_aggregate();
597 result
598 }
599
600 fn read_boolean(&mut self, _schema: &Schema<'_>) -> Result<bool, SerdeError> {
601 let text = self.take_text()?;
602 match text.as_ref() {
603 "true" => Ok(true),
604 "false" => Ok(false),
605 other => Err(SerdeError::custom(format!("invalid boolean: {other}"))),
606 }
607 }
608
609 fn read_byte(&mut self, _schema: &Schema<'_>) -> Result<i8, SerdeError> {
610 let text = self.take_text()?;
611 text.parse().map_err(|e| SerdeError::custom(format!("{e}")))
612 }
613
614 fn read_short(&mut self, _schema: &Schema<'_>) -> Result<i16, SerdeError> {
615 let text = self.take_text()?;
616 text.parse().map_err(|e| SerdeError::custom(format!("{e}")))
617 }
618
619 fn read_integer(&mut self, _schema: &Schema<'_>) -> Result<i32, SerdeError> {
620 let text = self.take_text()?;
621 text.parse().map_err(|e| SerdeError::custom(format!("{e}")))
622 }
623
624 fn read_long(&mut self, _schema: &Schema<'_>) -> Result<i64, SerdeError> {
625 let text = self.take_text()?;
626 text.parse().map_err(|e| SerdeError::custom(format!("{e}")))
627 }
628
629 fn read_float(&mut self, _schema: &Schema<'_>) -> Result<f32, SerdeError> {
630 let text = self.take_text()?;
631 match text.as_ref() {
632 "NaN" => Ok(f32::NAN),
633 "Infinity" => Ok(f32::INFINITY),
634 "-Infinity" => Ok(f32::NEG_INFINITY),
635 _ => text.parse().map_err(|e| SerdeError::custom(format!("{e}"))),
636 }
637 }
638
639 fn read_double(&mut self, _schema: &Schema<'_>) -> Result<f64, SerdeError> {
640 let text = self.take_text()?;
641 match text.as_ref() {
642 "NaN" => Ok(f64::NAN),
643 "Infinity" => Ok(f64::INFINITY),
644 "-Infinity" => Ok(f64::NEG_INFINITY),
645 _ => text.parse().map_err(|e| SerdeError::custom(format!("{e}"))),
646 }
647 }
648
649 fn read_big_integer(&mut self, _schema: &Schema<'_>) -> Result<BigInteger, SerdeError> {
650 let text = self.take_text()?;
651 text.parse().map_err(|e| SerdeError::custom(format!("{e}")))
652 }
653
654 fn read_big_decimal(&mut self, _schema: &Schema<'_>) -> Result<BigDecimal, SerdeError> {
655 let text = self.take_text()?;
656 text.parse().map_err(|e| SerdeError::custom(format!("{e}")))
657 }
658
659 fn read_string(&mut self, _schema: &Schema<'_>) -> Result<String, SerdeError> {
660 let text = self.take_text()?;
661 Ok(text.into_owned())
662 }
663
664 fn read_blob(&mut self, _schema: &Schema<'_>) -> Result<Blob, SerdeError> {
665 let text = self.take_text()?;
666 let bytes = aws_smithy_types::base64::decode(text.as_ref())
667 .map_err(|e| SerdeError::custom(format!("{e}")))?;
668 Ok(Blob::new(bytes))
669 }
670
671 fn read_timestamp(&mut self, schema: &Schema<'_>) -> Result<DateTime, SerdeError> {
672 let text = self.take_text()?;
673 let format = self.resolve_timestamp_format(schema)?;
674 DateTime::from_str(text.as_ref(), format).map_err(|e| SerdeError::custom(format!("{e}")))
675 }
676
677 fn read_document(&mut self, _schema: &Schema<'_>) -> Result<SmithyDocument, SerdeError> {
678 Err(SerdeError::custom(
679 "document types are not supported by REST XML",
680 ))
681 }
682
683 fn read_string_list(&mut self, _schema: &Schema<'_>) -> Result<Vec<String>, SerdeError> {
710 self.enter_aggregate()?;
711 let result = (|| -> Result<Vec<String>, SerdeError> {
716 let mut doc = self.document()?;
717 let mut root = doc
718 .root_element()
719 .map_err(|e| SerdeError::custom(e.to_string()))?;
720 let mut out = Vec::new();
721 while let Some(mut child_scope) = root.next_tag() {
722 let text = decode::try_data(&mut child_scope)
723 .map_err(|e| SerdeError::custom(e.to_string()))?;
724 out.push(text.into_owned());
725 }
726 Ok(out)
727 })();
728 self.leave_aggregate();
729 result
730 }
731
732 fn read_blob_list(&mut self, _schema: &Schema<'_>) -> Result<Vec<Blob>, SerdeError> {
733 use aws_smithy_types::base64;
734 self.enter_aggregate()?;
735 let result = (|| -> Result<Vec<Blob>, SerdeError> {
736 let mut doc = self.document()?;
737 let mut root = doc
738 .root_element()
739 .map_err(|e| SerdeError::custom(e.to_string()))?;
740 let mut out = Vec::new();
741 while let Some(mut child_scope) = root.next_tag() {
742 let text = decode::try_data(&mut child_scope)
743 .map_err(|e| SerdeError::custom(e.to_string()))?;
744 let bytes = base64::decode(text.as_ref())
745 .map_err(|e| SerdeError::custom(format!("invalid base64: {e}")))?;
746 out.push(Blob::new(bytes));
747 }
748 Ok(out)
749 })();
750 self.leave_aggregate();
751 result
752 }
753
754 fn read_integer_list(&mut self, _schema: &Schema<'_>) -> Result<Vec<i32>, SerdeError> {
755 self.enter_aggregate()?;
756 let result = (|| -> Result<Vec<i32>, SerdeError> {
757 let mut doc = self.document()?;
758 let mut root = doc
759 .root_element()
760 .map_err(|e| SerdeError::custom(e.to_string()))?;
761 let mut out = Vec::new();
762 while let Some(mut child_scope) = root.next_tag() {
763 let text = decode::try_data(&mut child_scope)
764 .map_err(|e| SerdeError::custom(e.to_string()))?;
765 let v: i32 = text
766 .parse()
767 .map_err(|e| SerdeError::custom(format!("{e}")))?;
768 out.push(v);
769 }
770 Ok(out)
771 })();
772 self.leave_aggregate();
773 result
774 }
775
776 fn read_long_list(&mut self, _schema: &Schema<'_>) -> Result<Vec<i64>, SerdeError> {
777 self.enter_aggregate()?;
778 let result = (|| -> Result<Vec<i64>, SerdeError> {
779 let mut doc = self.document()?;
780 let mut root = doc
781 .root_element()
782 .map_err(|e| SerdeError::custom(e.to_string()))?;
783 let mut out = Vec::new();
784 while let Some(mut child_scope) = root.next_tag() {
785 let text = decode::try_data(&mut child_scope)
786 .map_err(|e| SerdeError::custom(e.to_string()))?;
787 let v: i64 = text
788 .parse()
789 .map_err(|e| SerdeError::custom(format!("{e}")))?;
790 out.push(v);
791 }
792 Ok(out)
793 })();
794 self.leave_aggregate();
795 result
796 }
797
798 fn read_string_string_map(
799 &mut self,
800 schema: &Schema<'_>,
801 ) -> Result<std::collections::HashMap<String, String>, SerdeError> {
802 let key_name = schema
803 .key()
804 .and_then(|k| k.xml_name().map(|t| t.value()))
805 .unwrap_or("key");
806 let value_name = schema
807 .member()
808 .and_then(|v| v.xml_name().map(|t| t.value()))
809 .unwrap_or("value");
810
811 self.enter_aggregate()?;
812 let result = (|| -> Result<std::collections::HashMap<String, String>, SerdeError> {
813 let mut doc = self.document()?;
814 let mut root = doc
815 .root_element()
816 .map_err(|e| SerdeError::custom(e.to_string()))?;
817 let mut out = std::collections::HashMap::new();
818 while let Some(mut entry_scope) = root.next_tag() {
819 let mut k: Option<String> = None;
820 let mut v: Option<String> = None;
821 while let Some(mut field_scope) = entry_scope.next_tag() {
822 let local = field_scope.start_el().local();
823 let (is_key, is_value) = (local == key_name, local == value_name);
824 if is_key {
825 let text = decode::try_data(&mut field_scope)
826 .map_err(|e| SerdeError::custom(e.to_string()))?;
827 k = Some(text.into_owned());
828 } else if is_value {
829 let text = decode::try_data(&mut field_scope)
830 .map_err(|e| SerdeError::custom(e.to_string()))?;
831 v = Some(text.into_owned());
832 }
833 }
834 if let (Some(k), Some(v)) = (k, v) {
835 out.insert(k, v);
836 }
837 }
838 Ok(out)
839 })();
840 self.leave_aggregate();
841 result
842 }
843
844 fn is_null(&self) -> bool {
845 false
848 }
849
850 fn skip_value(&mut self) -> Result<(), SerdeError> {
856 Ok(())
857 }
858
859 fn container_size(&self) -> Option<usize> {
860 None
861 }
862}
863
864#[cfg(test)]
865mod tests {
866 use super::*;
867 use aws_smithy_schema::{shape_id, Schema, ShapeType};
868
869 static STRING_MEMBER: Schema<'static> =
870 Schema::new_member(shape_id!("test", "S$v"), ShapeType::String, "v", 0);
871
872 #[test]
873 fn strict_roots_only_validate_document_boundary() {
874 static CHILD: Schema<'static> = Schema::new_struct(
875 aws_smithy_schema::shape_id!("test", "Child"),
876 aws_smithy_schema::ShapeType::Structure,
877 &[],
878 );
879 static RENAMED: Schema<'static> = Schema::new_member(
880 aws_smithy_schema::shape_id!("test", "Root", "child"),
881 aws_smithy_schema::ShapeType::Structure,
882 "child",
883 0,
884 )
885 .with_xml_name("Renamed");
886 static ROOT: Schema<'static> = Schema::new_struct(
887 aws_smithy_schema::shape_id!("test", "Synthetic"),
888 aws_smithy_schema::ShapeType::Structure,
889 &[&RENAMED],
890 )
891 .with_original_name("Original")
892 .with_xml_name("WireRoot");
893 for strict in [false, true] {
894 let settings = Arc::new(
895 XmlCodecSettings::builder()
896 .enforce_strictness(strict)
897 .build(),
898 );
899 for (input, matches) in [
900 (b"<WireRoot><Renamed/></WireRoot>".as_slice(), true),
901 (b"<Wrong><Renamed/></Wrong>", false),
902 ] {
903 let mut visited = false;
904 let result = XmlDeserializer::new(input, settings.clone()).read_struct(
905 &ROOT,
906 &mut |_, d| {
907 visited = true;
908 d.read_struct(&CHILD, &mut |_, _| Ok(()))
909 },
910 );
911 assert_eq!(result.is_ok(), !strict || matches);
912 assert_eq!(visited, !strict || matches);
913 }
914 }
915 }
916
917 #[test]
918 fn read_string_from_text_state() {
919 let settings = Arc::new(XmlCodecSettings::default());
920 let mut deser = XmlDeserializer::from_text(Cow::Borrowed("hello"), settings);
921 let result = deser.read_string(&STRING_MEMBER).unwrap();
922 assert_eq!(result, "hello");
923 }
924
925 #[test]
926 fn read_string_from_doc_text_content() {
927 static V_MEMBER: Schema<'static> =
931 Schema::new_member(shape_id!("test", "X$v"), ShapeType::String, "v", 0);
932 let xml = b"<root>world</root>";
933 let settings = Arc::new(XmlCodecSettings::default());
934 let mut deser = XmlDeserializer::new(xml, settings);
935 let result = deser.read_string(&V_MEMBER).unwrap();
936 assert_eq!(result, "world");
937 }
938
939 #[test]
940 fn is_null_always_false() {
941 let settings = Arc::new(XmlCodecSettings::default());
942 let deser = XmlDeserializer::new(b"<r/>", settings);
943 assert!(!deser.is_null());
944 }
945
946 static NAME_MEMBER: Schema<'static> = Schema::new_member(
949 shape_id!("test", "Person$name"),
950 ShapeType::String,
951 "name",
952 0,
953 );
954 static AGE_MEMBER: Schema<'static> =
955 Schema::new_member(shape_id!("test", "Person$age"), ShapeType::String, "age", 1);
956 static RENAMED_MEMBER: Schema<'static> = Schema::new_member(
957 shape_id!("test", "Person$nick"),
958 ShapeType::String,
959 "nick",
960 2,
961 )
962 .with_xml_name("Nickname");
963
964 static PERSON_SCHEMA: Schema<'static> = Schema::new_struct(
965 shape_id!("test", "Person"),
966 ShapeType::Structure,
967 &[&NAME_MEMBER, &AGE_MEMBER, &RENAMED_MEMBER],
968 );
969
970 #[test]
971 fn read_struct_dispatches_members() {
972 let xml = b"<Person><name>Alice</name><age>30</age></Person>";
973 let settings = Arc::new(XmlCodecSettings::default());
974 let mut deser = XmlDeserializer::new(xml, settings);
975
976 let mut name = String::new();
977 let mut age = String::new();
978 deser
979 .read_struct(&PERSON_SCHEMA, &mut |member, d| {
980 match member.member_name().unwrap() {
981 "name" => name = d.read_string(member)?,
982 "age" => age = d.read_string(member)?,
983 _ => {}
984 }
985 Ok(())
986 })
987 .unwrap();
988
989 assert_eq!(name, "Alice");
990 assert_eq!(age, "30");
991 }
992
993 #[test]
994 fn read_struct_skips_unknown_elements() {
995 let xml = b"<Person><unknown>x</unknown><name>Bob</name></Person>";
996 let settings = Arc::new(XmlCodecSettings::default());
997 let mut deser = XmlDeserializer::new(xml, settings);
998
999 let mut name = String::new();
1000 deser
1001 .read_struct(&PERSON_SCHEMA, &mut |member, d| {
1002 if member.member_name() == Some("name") {
1003 name = d.read_string(member)?;
1004 }
1005 Ok(())
1006 })
1007 .unwrap();
1008
1009 assert_eq!(name, "Bob");
1010 }
1011
1012 #[test]
1016 fn colliding_element_names_resolve_to_the_first_member() {
1017 static STATUS: Schema<'static> = Schema::new_member(
1018 shape_id!("ns", "S", "status"),
1019 ShapeType::String,
1020 "status",
1021 0,
1022 );
1023 static STATUS_TEXT: Schema<'static> = Schema::new_member(
1024 shape_id!("ns", "S", "statusText"),
1025 ShapeType::String,
1026 "statusText",
1027 1,
1028 )
1029 .with_xml_name("status");
1030 static OTHER: Schema<'static> =
1031 Schema::new_member(shape_id!("ns", "S", "other"), ShapeType::String, "other", 2);
1032 static S: Schema<'static> = Schema::new_struct(
1033 shape_id!("ns", "S"),
1034 ShapeType::Structure,
1035 &[&STATUS, &STATUS_TEXT, &OTHER],
1036 );
1037
1038 fn read(xml: &[u8]) -> Vec<(usize, String)> {
1039 let mut deser = XmlDeserializer::new(xml, Arc::new(XmlCodecSettings::default()));
1040 let mut seen = Vec::new();
1041 deser
1042 .read_struct(&S, &mut |member, d| {
1043 seen.push((member.member_index().unwrap(), d.read_string(member)?));
1044 Ok(())
1045 })
1046 .unwrap();
1047 seen
1048 }
1049
1050 assert_eq!(
1051 read(b"<S><status>a</status><status>b</status></S>"),
1052 [(0, "a".to_owned()), (0, "b".to_owned())]
1053 );
1054 assert_eq!(
1055 read(b"<S><other>x</other><status>a</status></S>"),
1056 [(2, "x".to_owned()), (0, "a".to_owned())]
1057 );
1058 }
1059
1060 #[test]
1061 fn read_struct_resolves_xml_name() {
1062 let xml = b"<Person><Nickname>Ally</Nickname></Person>";
1063 let settings = Arc::new(XmlCodecSettings::default());
1064 let mut deser = XmlDeserializer::new(xml, settings);
1065
1066 let mut nick = String::new();
1067 deser
1068 .read_struct(&PERSON_SCHEMA, &mut |member, d| {
1069 if member.member_name() == Some("nick") {
1070 nick = d.read_string(member)?;
1071 }
1072 Ok(())
1073 })
1074 .unwrap();
1075
1076 assert_eq!(nick, "Ally");
1077 }
1078
1079 static ATTR_MEMBER: Schema<'static> =
1082 Schema::new_member(shape_id!("test", "X$id"), ShapeType::String, "id", 0)
1083 .with_xml_attribute();
1084 static ELEM_MEMBER: Schema<'static> =
1085 Schema::new_member(shape_id!("test", "X$name"), ShapeType::String, "name", 1);
1086
1087 static X_SCHEMA: Schema<'static> = Schema::new_struct(
1088 shape_id!("test", "X"),
1089 ShapeType::Structure,
1090 &[&ATTR_MEMBER, &ELEM_MEMBER],
1091 );
1092
1093 #[test]
1094 fn read_struct_dispatches_attributes() {
1095 let xml = b"<X id=\"42\"><name>hello</name></X>";
1096 let settings = Arc::new(XmlCodecSettings::default());
1097 let mut deser = XmlDeserializer::new(xml, settings);
1098
1099 let mut id = String::new();
1100 let mut name = String::new();
1101 deser
1102 .read_struct(&X_SCHEMA, &mut |member, d| {
1103 match member.member_name().unwrap() {
1104 "id" => id = d.read_string(member)?,
1105 "name" => name = d.read_string(member)?,
1106 _ => {}
1107 }
1108 Ok(())
1109 })
1110 .unwrap();
1111
1112 assert_eq!(id, "42");
1113 assert_eq!(name, "hello");
1114 }
1115
1116 #[test]
1119 fn read_list_wrapped() {
1120 let xml = b"<items><member>a</member><member>b</member></items>";
1121 let settings = Arc::new(XmlCodecSettings::default());
1122 let mut deser = XmlDeserializer::new(xml, settings);
1123
1124 static LIST_MEMBER: Schema<'static> = Schema::new_member(
1125 shape_id!("test", "L$member"),
1126 ShapeType::String,
1127 "member",
1128 0,
1129 );
1130 static LIST_SCHEMA: Schema<'static> =
1131 Schema::new_list(shape_id!("test", "L"), &LIST_MEMBER);
1132
1133 let mut items = Vec::new();
1134 deser
1135 .read_list(&LIST_SCHEMA, &mut |d| {
1136 items.push(d.read_string(&LIST_MEMBER)?);
1137 Ok(())
1138 })
1139 .unwrap();
1140
1141 assert_eq!(items, vec!["a", "b"]);
1142 }
1143
1144 #[test]
1145 fn read_map_wrapped() {
1146 let xml = b"<myMap><entry><key>k1</key><value>v1</value></entry><entry><key>k2</key><value>v2</value></entry></myMap>";
1147 let settings = Arc::new(XmlCodecSettings::default());
1148 let mut deser = XmlDeserializer::new(xml, settings);
1149
1150 static MAP_KEY: Schema<'static> =
1151 Schema::new_member(shape_id!("test", "M$key"), ShapeType::String, "key", 0);
1152 static MAP_VALUE: Schema<'static> =
1153 Schema::new_member(shape_id!("test", "M$value"), ShapeType::String, "value", 0);
1154 static MAP_SCHEMA: Schema<'static> =
1155 Schema::new_map(shape_id!("test", "M"), &MAP_KEY, &MAP_VALUE);
1156
1157 let mut entries = Vec::new();
1158 deser
1159 .read_map(&MAP_SCHEMA, &mut |k, d| {
1160 entries.push((k, d.read_string(&MAP_VALUE)?));
1161 Ok(())
1162 })
1163 .unwrap();
1164
1165 assert_eq!(
1166 entries,
1167 vec![
1168 ("k1".to_owned(), "v1".to_owned()),
1169 ("k2".to_owned(), "v2".to_owned())
1170 ]
1171 );
1172 }
1173
1174 #[test]
1175 fn read_map_with_renamed_key_value() {
1176 let xml = b"<m><entry><Attribute>a</Attribute><Setting>s</Setting></entry></m>";
1177 let settings = Arc::new(XmlCodecSettings::default());
1178 let mut deser = XmlDeserializer::new(xml, settings);
1179
1180 static MAP_KEY: Schema<'static> =
1181 Schema::new_member(shape_id!("test", "M$key"), ShapeType::String, "key", 0)
1182 .with_xml_name("Attribute");
1183 static MAP_VALUE: Schema<'static> =
1184 Schema::new_member(shape_id!("test", "M$value"), ShapeType::String, "value", 0)
1185 .with_xml_name("Setting");
1186 static MAP_SCHEMA: Schema<'static> =
1187 Schema::new_map(shape_id!("test", "M"), &MAP_KEY, &MAP_VALUE);
1188
1189 let mut entries = Vec::new();
1190 deser
1191 .read_map(&MAP_SCHEMA, &mut |k, d| {
1192 entries.push((k, d.read_string(&MAP_VALUE)?));
1193 Ok(())
1194 })
1195 .unwrap();
1196
1197 assert_eq!(entries, vec![("a".to_owned(), "s".to_owned())]);
1198 }
1199
1200 #[test]
1203 fn read_struct_flattened_list() {
1204 let xml = b"<S><name>hi</name><item>a</item><item>b</item></S>";
1206 let settings = Arc::new(XmlCodecSettings::default());
1207 let mut deser = XmlDeserializer::new(xml, settings);
1208
1209 static S_NAME: Schema<'static> =
1210 Schema::new_member(shape_id!("test", "S$name"), ShapeType::String, "name", 0);
1211 static S_ITEMS: Schema<'static> =
1212 Schema::new_member(shape_id!("test", "S$items"), ShapeType::List, "items", 1)
1213 .with_xml_flattened()
1214 .with_xml_name("item");
1215 static S_SCHEMA: Schema<'static> = Schema::new_struct(
1216 shape_id!("test", "S"),
1217 ShapeType::Structure,
1218 &[&S_NAME, &S_ITEMS],
1219 );
1220
1221 let mut name = String::new();
1222 let mut items = Vec::new();
1223 deser
1224 .read_struct(&S_SCHEMA, &mut |member, d| {
1225 match member.member_name().unwrap() {
1226 "name" => name = d.read_string(member)?,
1227 "items" => d.read_list(member, &mut |d| {
1228 items.push(d.read_string(member)?);
1229 Ok(())
1230 })?,
1231 _ => {}
1232 }
1233 Ok(())
1234 })
1235 .unwrap();
1236
1237 assert_eq!(name, "hi");
1238 assert_eq!(items, vec!["a", "b"]);
1239 }
1240
1241 #[test]
1242 fn read_struct_flattened_list_intermixed() {
1243 let xml = b"<S><item>x</item><name>n</name><item>y</item></S>";
1245 let settings = Arc::new(XmlCodecSettings::default());
1246 let mut deser = XmlDeserializer::new(xml, settings);
1247
1248 static S_NAME: Schema<'static> =
1249 Schema::new_member(shape_id!("test", "S$name"), ShapeType::String, "name", 0);
1250 static S_ITEMS: Schema<'static> =
1251 Schema::new_member(shape_id!("test", "S$items"), ShapeType::List, "items", 1)
1252 .with_xml_flattened()
1253 .with_xml_name("item");
1254 static S_SCHEMA: Schema<'static> = Schema::new_struct(
1255 shape_id!("test", "S"),
1256 ShapeType::Structure,
1257 &[&S_NAME, &S_ITEMS],
1258 );
1259
1260 let mut name = String::new();
1261 let mut items = Vec::new();
1262 deser
1263 .read_struct(&S_SCHEMA, &mut |member, d| {
1264 match member.member_name().unwrap() {
1265 "name" => name = d.read_string(member)?,
1266 "items" => d.read_list(member, &mut |d| {
1267 items.push(d.read_string(member)?);
1268 Ok(())
1269 })?,
1270 _ => {}
1271 }
1272 Ok(())
1273 })
1274 .unwrap();
1275
1276 assert_eq!(name, "n");
1277 assert_eq!(items, vec!["x", "y"]);
1278 }
1279
1280 #[test]
1283 fn read_scalars() {
1284 let settings = Arc::new(XmlCodecSettings::default());
1285
1286 let mut d = XmlDeserializer::from_text(Cow::Borrowed("true"), settings.clone());
1287 assert!(d.read_boolean(&STRING_MEMBER).unwrap());
1288
1289 let mut d = XmlDeserializer::from_text(Cow::Borrowed("-42"), settings.clone());
1290 assert_eq!(d.read_integer(&STRING_MEMBER).unwrap(), -42);
1291
1292 let mut d = XmlDeserializer::from_text(Cow::Borrowed("NaN"), settings.clone());
1293 assert!(d.read_float(&STRING_MEMBER).unwrap().is_nan());
1294
1295 let mut d = XmlDeserializer::from_text(Cow::Borrowed("Infinity"), settings.clone());
1296 assert_eq!(d.read_double(&STRING_MEMBER).unwrap(), f64::INFINITY);
1297
1298 let mut d = XmlDeserializer::from_text(Cow::Borrowed("aGVsbG8="), settings.clone());
1299 assert_eq!(d.read_blob(&STRING_MEMBER).unwrap().as_ref(), b"hello");
1300
1301 let mut d =
1302 XmlDeserializer::from_text(Cow::Borrowed("2023-04-01T12:00:00Z"), settings.clone());
1303 let ts = d.read_timestamp(&STRING_MEMBER).unwrap();
1304 assert_eq!(ts.secs(), 1680350400);
1305 }
1306
1307 #[test]
1308 fn read_document_returns_error() {
1309 let settings = Arc::new(XmlCodecSettings::default());
1310 let mut deser = XmlDeserializer::from_text(Cow::Borrowed("x"), settings);
1311 assert_eq!(
1312 deser.read_document(&STRING_MEMBER).unwrap_err().to_string(),
1313 "document types are not supported by REST XML"
1314 );
1315 }
1316
1317 #[test]
1327 fn read_struct_empty_body_errors() {
1328 let settings = Arc::new(XmlCodecSettings::default());
1329 let mut deser = XmlDeserializer::new(b"", settings);
1330 let err = deser
1331 .read_struct(&PERSON_SCHEMA, &mut |_member, _d| Ok(()))
1332 .expect_err("empty body must be rejected by read_struct");
1333 let _ = format!("{err}");
1334 }
1335
1336 #[test]
1337 fn read_list_empty_body_errors() {
1338 let settings = Arc::new(XmlCodecSettings::default());
1339 let mut deser = XmlDeserializer::new(b"", settings);
1340 deser
1341 .read_list(&PERSON_SCHEMA, &mut |_d| Ok(()))
1342 .expect_err("empty body must be rejected by read_list");
1343 }
1344
1345 #[test]
1346 fn read_map_empty_body_errors() {
1347 let settings = Arc::new(XmlCodecSettings::default());
1348 let mut deser = XmlDeserializer::new(b"", settings);
1349 deser
1350 .read_map(&PERSON_SCHEMA, &mut |_k, _d| Ok(()))
1351 .expect_err("empty body must be rejected by read_map");
1352 }
1353
1354 fn settings_with_max_depth(n: u32) -> Arc<XmlCodecSettings> {
1360 Arc::new(XmlCodecSettings::builder().max_depth(n).build())
1361 }
1362
1363 fn nested_struct_xml(depth: u32) -> Vec<u8> {
1365 let mut s = String::new();
1366 for _ in 0..depth {
1367 s.push_str("<r>");
1368 }
1369 s.push('v');
1370 for _ in 0..depth {
1371 s.push_str("</r>");
1372 }
1373 s.into_bytes()
1374 }
1375
1376 #[test]
1377 fn read_struct_rejects_overdeep_payloads() {
1378 static R_MEMBER_SELF: Schema<'static> =
1382 Schema::new_member(shape_id!("test", "R$r"), ShapeType::Structure, "r", 0);
1383 static R_SCHEMA: Schema<'static> = Schema::new_struct(
1384 shape_id!("test", "R"),
1385 ShapeType::Structure,
1386 &[&R_MEMBER_SELF],
1387 );
1388
1389 let max = 4;
1391 let xml = nested_struct_xml(max + 2);
1392 let mut deser = XmlDeserializer::new(&xml, settings_with_max_depth(max));
1393
1394 fn consume(_m: &Schema<'_>, d: &mut dyn ShapeDeserializer) -> Result<(), SerdeError> {
1396 d.read_struct(&R_SCHEMA, &mut consume)
1397 }
1398 let err = deser
1399 .read_struct(&R_SCHEMA, &mut consume)
1400 .expect_err("must reject payload exceeding max_depth");
1401 assert!(
1402 format!("{err}").contains("maximum nesting depth exceeded"),
1403 "expected depth-exceeded error, got: {err}"
1404 );
1405 }
1406
1407 #[test]
1408 fn read_struct_accepts_payloads_up_to_max_depth() {
1409 static R_MEMBER_SELF: Schema<'static> =
1414 Schema::new_member(shape_id!("test", "R$r"), ShapeType::Structure, "r", 0);
1415 static R_SCHEMA: Schema<'static> = Schema::new_struct(
1416 shape_id!("test", "R"),
1417 ShapeType::Structure,
1418 &[&R_MEMBER_SELF],
1419 );
1420
1421 let max = 4;
1425 let xml = nested_struct_xml(max);
1426 let mut deser = XmlDeserializer::new(&xml, settings_with_max_depth(max));
1427
1428 let mut depth_seen = 0u32;
1429 fn consume(
1430 _m: &Schema<'_>,
1431 d: &mut dyn ShapeDeserializer,
1432 depth_seen: &mut u32,
1433 ) -> Result<(), SerdeError> {
1434 *depth_seen += 1;
1435 d.read_struct(&R_SCHEMA, &mut |m, d2| consume(m, d2, depth_seen))
1436 }
1437
1438 deser
1439 .read_struct(&R_SCHEMA, &mut |m, d| consume(m, d, &mut depth_seen))
1440 .expect("payload at exactly max_depth must succeed");
1441 }
1442
1443 #[test]
1444 fn read_list_rejects_overdeep_payloads() {
1445 static L_MEMBER: Schema<'static> =
1447 Schema::new_member(shape_id!("test", "L$member"), ShapeType::List, "member", 0);
1448 static L_SCHEMA: Schema<'static> = Schema::new_list(shape_id!("test", "L"), &L_MEMBER);
1449
1450 let max = 3;
1451 let xml = b"<l><l><l><l><l/></l></l></l></l>";
1453 let mut deser = XmlDeserializer::new(xml, settings_with_max_depth(max));
1454
1455 fn consume(d: &mut dyn ShapeDeserializer) -> Result<(), SerdeError> {
1456 d.read_list(&L_SCHEMA, &mut consume)
1457 }
1458 let err = deser
1459 .read_list(&L_SCHEMA, &mut consume)
1460 .expect_err("nested-list payload exceeding max_depth must error");
1461 assert!(
1462 format!("{err}").contains("maximum nesting depth exceeded"),
1463 "expected depth-exceeded error, got: {err}"
1464 );
1465 }
1466
1467 #[test]
1468 fn depth_resets_between_sibling_reads() {
1469 static R_MEMBER_SELF: Schema<'static> =
1472 Schema::new_member(shape_id!("test", "R$r"), ShapeType::Structure, "r", 0);
1473 static R_SCHEMA: Schema<'static> = Schema::new_struct(
1474 shape_id!("test", "R"),
1475 ShapeType::Structure,
1476 &[&R_MEMBER_SELF],
1477 );
1478
1479 let max = 4;
1480 let xml = nested_struct_xml(2);
1481 let mut deser = XmlDeserializer::new(&xml, settings_with_max_depth(max));
1482
1483 deser
1485 .read_struct(&R_SCHEMA, &mut |_m, d| {
1486 d.read_struct(&R_SCHEMA, &mut |_, _| Ok(()))
1487 })
1488 .expect("first read at depth=2 must succeed");
1489
1490 deser
1493 .read_struct(&R_SCHEMA, &mut |_m, d| {
1494 d.read_struct(&R_SCHEMA, &mut |_, _| Ok(()))
1495 })
1496 .expect("sibling read on the same deserializer must succeed");
1497 }
1498
1499 #[test]
1500 fn depth_resets_after_consumer_error() {
1501 static L_MEMBER: Schema<'static> = Schema::new_member(
1514 shape_id!("test", "L$member"),
1515 ShapeType::String,
1516 "member",
1517 0,
1518 );
1519 static L_SCHEMA: Schema<'static> = Schema::new_list(shape_id!("test", "L"), &L_MEMBER);
1520
1521 let max = 2;
1527 let xml = b"<l><member>a</member><member>b</member></l>";
1528 let mut deser = XmlDeserializer::new(xml, settings_with_max_depth(max));
1529
1530 let mut force_err = |_: &mut dyn ShapeDeserializer| -> Result<(), SerdeError> {
1532 Err(SerdeError::custom("forced"))
1533 };
1534 deser
1535 .read_list(&L_SCHEMA, &mut force_err)
1536 .expect_err("forced consumer error must propagate");
1537
1538 for i in 0..(max as usize + 5) {
1542 deser
1543 .read_list(&L_SCHEMA, &mut force_err)
1544 .expect_err(&format!("forced consumer error must propagate (iter {i})"));
1545 }
1546
1547 let mut count = 0usize;
1550 deser
1551 .read_list(&L_SCHEMA, &mut |d| {
1552 count += 1;
1553 d.read_string(&L_MEMBER).map(|_| ())
1554 })
1555 .expect("subsequent successful read must not be poisoned by prior errors");
1556 assert_eq!(count, 2);
1557 }
1558
1559 #[test]
1560 fn read_struct_unwrapped_output_with_prolog() {
1561 static MEMBER: Schema<'static> = Schema::new_member(
1575 shape_id!("test", "U$location"),
1576 ShapeType::String,
1577 "LocationConstraint",
1578 0,
1579 );
1580 static SCHEMA: Schema<'static> =
1581 Schema::new_struct(shape_id!("test", "U"), ShapeType::Structure, &[&MEMBER])
1582 .with_xml_unwrapped_output();
1583
1584 let xml = b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<LocationConstraint>us-west-2</LocationConstraint>";
1585 let mut deser = XmlDeserializer::new(xml, Arc::new(XmlCodecSettings::default()));
1586
1587 let mut got = String::new();
1588 deser
1589 .read_struct(&SCHEMA, &mut |member, d| {
1590 got = d.read_string(member)?;
1591 Ok(())
1592 })
1593 .expect("unwrapped output must deserialize through prolog");
1594 assert_eq!(got, "us-west-2");
1595 }
1596
1597 #[test]
1609 fn find_element_slice_handles_multibyte_utf8() {
1610 static KEY_MEMBER: Schema<'static> =
1615 Schema::new_member(shape_id!("test", "Wrapper$k"), ShapeType::Map, "entries", 0);
1616 static MAP_SCHEMA: Schema<'static> = Schema::new_map(
1617 shape_id!("test", "MyMap"),
1618 &aws_smithy_schema::prelude::STRING,
1619 &aws_smithy_schema::prelude::STRING,
1620 );
1621 static WRAPPER: Schema<'static> = Schema::new_struct(
1622 shape_id!("test", "Wrapper"),
1623 ShapeType::Structure,
1624 &[&KEY_MEMBER],
1625 );
1626 let _ = MAP_SCHEMA; let xml =
1629 "<Wrapper><entries><entry><key>Б</key><value></value></entry></entries></Wrapper>";
1630
1631 let settings = Arc::new(XmlCodecSettings::default());
1632 let mut deser = XmlDeserializer::new(xml.as_bytes(), settings);
1633
1634 let mut entries = Vec::new();
1635 deser
1636 .read_struct(&WRAPPER, &mut |member, d| {
1637 d.read_map(member, &mut |k, d| {
1638 entries.push((k, d.read_string(&aws_smithy_schema::prelude::STRING)?));
1639 Ok(())
1640 })
1641 })
1642 .expect("must not panic on multi-byte UTF-8 inside map elements");
1643
1644 assert_eq!(entries, vec![("Б".to_owned(), String::new())]);
1645 }
1646
1647 #[test]
1648 fn find_depth2_by_predicate_wrapped() {
1649 let xml = b"<Resp><FooResult><A>1</A></FooResult><Metadata/></Resp>";
1650 let got = find_depth2_element_slice_by(xml, |n| n.ends_with("Result"));
1651 assert_eq!(got, Some(&b"<FooResult><A>1</A></FooResult>"[..]));
1652 }
1653
1654 #[test]
1655 fn find_depth2_by_predicate_self_closing() {
1656 let xml = b"<Resp><FooResult/><Metadata><Id>r</Id></Metadata></Resp>";
1659 let got = find_depth2_element_slice_by(xml, |n| n.ends_with("Result"));
1660 assert_eq!(got, Some(&b"<FooResult/>"[..]));
1661 }
1662
1663 #[test]
1664 fn find_depth2_by_predicate_root_match() {
1665 let xml = b"<Error><Code>Boom</Code></Error>";
1667 let got = find_depth2_element_slice_by(xml, |n| n == "Error");
1668 assert_eq!(got, Some(&xml[..]));
1669 }
1670
1671 #[test]
1672 fn find_depth2_by_predicate_no_match() {
1673 let xml = b"<Resp><Metadata/></Resp>";
1674 assert_eq!(find_depth2_element_slice_by(xml, |n| n == "Error"), None);
1675 }
1676
1677 #[test]
1678 fn find_depth2_by_predicate_invalid_xml() {
1679 assert_eq!(find_depth2_element_slice_by(b"not xml", |_| true), None);
1680 }
1681
1682 #[test]
1690 fn read_string_list_helper() {
1691 let xml = b"<items><member>a</member><member>b</member><member></member></items>";
1692 let settings = Arc::new(XmlCodecSettings::default());
1693 let mut deser = XmlDeserializer::new(xml, settings);
1694
1695 static LIST_MEMBER: Schema<'static> = Schema::new_member(
1696 shape_id!("test", "L$member"),
1697 ShapeType::String,
1698 "member",
1699 0,
1700 );
1701 static LIST_SCHEMA: Schema<'static> =
1702 Schema::new_list(shape_id!("test", "L"), &LIST_MEMBER);
1703
1704 let out = deser.read_string_list(&LIST_SCHEMA).unwrap();
1705 assert_eq!(out, vec!["a".to_owned(), "b".to_owned(), String::new()]);
1706 }
1707
1708 #[test]
1709 fn read_string_list_helper_renamed_member_name() {
1710 let xml = b"<items><Item>x</Item><Item>y</Item></items>";
1715 let settings = Arc::new(XmlCodecSettings::default());
1716 let mut deser = XmlDeserializer::new(xml, settings);
1717
1718 static LIST_MEMBER: Schema<'static> = Schema::new_member(
1719 shape_id!("test", "L$member"),
1720 ShapeType::String,
1721 "member",
1722 0,
1723 );
1724 static LIST_SCHEMA: Schema<'static> =
1725 Schema::new_list(shape_id!("test", "L"), &LIST_MEMBER);
1726
1727 let out = deser.read_string_list(&LIST_SCHEMA).unwrap();
1728 assert_eq!(out, vec!["x".to_owned(), "y".to_owned()]);
1729 }
1730
1731 #[test]
1732 fn read_blob_list_helper() {
1733 let xml = b"<blobs><member>aGVsbG8=</member><member>d29ybGQ=</member></blobs>";
1735 let settings = Arc::new(XmlCodecSettings::default());
1736 let mut deser = XmlDeserializer::new(xml, settings);
1737
1738 static LIST_MEMBER: Schema<'static> =
1739 Schema::new_member(shape_id!("test", "B$member"), ShapeType::Blob, "member", 0);
1740 static LIST_SCHEMA: Schema<'static> =
1741 Schema::new_list(shape_id!("test", "B"), &LIST_MEMBER);
1742
1743 let out = deser.read_blob_list(&LIST_SCHEMA).unwrap();
1744 assert_eq!(out.len(), 2);
1745 assert_eq!(out[0].as_ref(), b"hello");
1746 assert_eq!(out[1].as_ref(), b"world");
1747 }
1748
1749 #[test]
1750 fn read_blob_list_helper_rejects_invalid_base64() {
1751 let xml = b"<blobs><member>!!!not-base64!!!</member></blobs>";
1752 let settings = Arc::new(XmlCodecSettings::default());
1753 let mut deser = XmlDeserializer::new(xml, settings);
1754
1755 static LIST_MEMBER: Schema<'static> =
1756 Schema::new_member(shape_id!("test", "B$member"), ShapeType::Blob, "member", 0);
1757 static LIST_SCHEMA: Schema<'static> =
1758 Schema::new_list(shape_id!("test", "B"), &LIST_MEMBER);
1759
1760 let err = deser.read_blob_list(&LIST_SCHEMA).unwrap_err();
1761 assert!(format!("{err}").contains("base64"));
1762 }
1763
1764 #[test]
1765 fn read_integer_list_helper() {
1766 let xml = b"<nums><member>1</member><member>-42</member><member>0</member></nums>";
1767 let settings = Arc::new(XmlCodecSettings::default());
1768 let mut deser = XmlDeserializer::new(xml, settings);
1769
1770 static LIST_MEMBER: Schema<'static> = Schema::new_member(
1771 shape_id!("test", "I$member"),
1772 ShapeType::Integer,
1773 "member",
1774 0,
1775 );
1776 static LIST_SCHEMA: Schema<'static> =
1777 Schema::new_list(shape_id!("test", "I"), &LIST_MEMBER);
1778
1779 let out = deser.read_integer_list(&LIST_SCHEMA).unwrap();
1780 assert_eq!(out, vec![1i32, -42, 0]);
1781 }
1782
1783 #[test]
1784 fn read_long_list_helper() {
1785 let xml = b"<nums><member>9223372036854775807</member><member>-1</member></nums>";
1786 let settings = Arc::new(XmlCodecSettings::default());
1787 let mut deser = XmlDeserializer::new(xml, settings);
1788
1789 static LIST_MEMBER: Schema<'static> =
1790 Schema::new_member(shape_id!("test", "Lo$member"), ShapeType::Long, "member", 0);
1791 static LIST_SCHEMA: Schema<'static> =
1792 Schema::new_list(shape_id!("test", "Lo"), &LIST_MEMBER);
1793
1794 let out = deser.read_long_list(&LIST_SCHEMA).unwrap();
1795 assert_eq!(out, vec![i64::MAX, -1]);
1796 }
1797
1798 #[test]
1799 fn read_string_string_map_helper() {
1800 let xml = b"<m><entry><key>a</key><value>1</value></entry>\
1801 <entry><key>b</key><value>2</value></entry></m>";
1802 let settings = Arc::new(XmlCodecSettings::default());
1803 let mut deser = XmlDeserializer::new(xml, settings);
1804
1805 static MAP_KEY: Schema<'static> =
1806 Schema::new_member(shape_id!("test", "M$key"), ShapeType::String, "key", 0);
1807 static MAP_VALUE: Schema<'static> =
1808 Schema::new_member(shape_id!("test", "M$value"), ShapeType::String, "value", 0);
1809 static MAP_SCHEMA: Schema<'static> =
1810 Schema::new_map(shape_id!("test", "M"), &MAP_KEY, &MAP_VALUE);
1811
1812 let out = deser.read_string_string_map(&MAP_SCHEMA).unwrap();
1813 assert_eq!(out.len(), 2);
1814 assert_eq!(out.get("a").map(String::as_str), Some("1"));
1815 assert_eq!(out.get("b").map(String::as_str), Some("2"));
1816 }
1817
1818 #[test]
1819 fn read_string_string_map_helper_with_renamed_key_value() {
1820 let xml = b"<m><entry><K>a</K><V>1</V></entry></m>";
1823 let settings = Arc::new(XmlCodecSettings::default());
1824 let mut deser = XmlDeserializer::new(xml, settings);
1825
1826 static MAP_KEY: Schema<'static> =
1827 Schema::new_member(shape_id!("test", "M2$key"), ShapeType::String, "key", 0)
1828 .with_xml_name("K");
1829 static MAP_VALUE: Schema<'static> =
1830 Schema::new_member(shape_id!("test", "M2$value"), ShapeType::String, "value", 0)
1831 .with_xml_name("V");
1832 static MAP_SCHEMA: Schema<'static> =
1833 Schema::new_map(shape_id!("test", "M2"), &MAP_KEY, &MAP_VALUE);
1834
1835 let out = deser.read_string_string_map(&MAP_SCHEMA).unwrap();
1836 assert_eq!(out.get("a").map(String::as_str), Some("1"));
1837 }
1838
1839 #[test]
1849 fn nested_struct_three_levels_deep() {
1850 static LEAF: Schema<'static> =
1851 Schema::new_member(shape_id!("t", "Inner"), ShapeType::String, "Leaf", 0);
1852 static INNER_SCHEMA: Schema<'static> =
1853 Schema::new_struct(shape_id!("t", "Inner"), ShapeType::Structure, &[&LEAF]);
1854 static INNER_MEMBER: Schema<'static> =
1855 Schema::new_member(shape_id!("t", "Middle"), ShapeType::Structure, "Inner", 0);
1856 static MIDDLE_SCHEMA: Schema<'static> = Schema::new_struct(
1857 shape_id!("t", "Middle"),
1858 ShapeType::Structure,
1859 &[&INNER_MEMBER],
1860 );
1861 static MIDDLE_MEMBER: Schema<'static> =
1862 Schema::new_member(shape_id!("t", "Outer"), ShapeType::Structure, "Middle", 0);
1863 static OUTER_SCHEMA: Schema<'static> = Schema::new_struct(
1864 shape_id!("t", "Outer"),
1865 ShapeType::Structure,
1866 &[&MIDDLE_MEMBER],
1867 );
1868 static OUTER_MEMBER: Schema<'static> =
1869 Schema::new_member(shape_id!("t", "Root"), ShapeType::Structure, "Outer", 0);
1870 static ROOT: Schema<'static> = Schema::new_struct(
1871 shape_id!("t", "Root"),
1872 ShapeType::Structure,
1873 &[&OUTER_MEMBER],
1874 );
1875
1876 let xml = b"<Root><Outer><Middle><Inner><Leaf>value</Leaf></Inner></Middle></Outer></Root>";
1877 let settings = Arc::new(XmlCodecSettings::default());
1878 let mut deser = XmlDeserializer::new(xml, settings);
1879 let mut leaf = String::new();
1880 deser
1881 .read_struct(&ROOT, &mut |outer_m, d_outer| {
1882 assert_eq!(outer_m.member_name(), Some("Outer"));
1883 d_outer.read_struct(&OUTER_SCHEMA, &mut |middle_m, d_middle| {
1884 assert_eq!(middle_m.member_name(), Some("Middle"));
1885 d_middle.read_struct(&MIDDLE_SCHEMA, &mut |inner_m, d_inner| {
1886 assert_eq!(inner_m.member_name(), Some("Inner"));
1887 d_inner.read_struct(&INNER_SCHEMA, &mut |leaf_m, d_leaf| {
1888 if leaf_m.member_name() == Some("Leaf") {
1889 leaf = d_leaf.read_string(leaf_m)?;
1890 }
1891 Ok(())
1892 })
1893 })
1894 })
1895 })
1896 .expect("3-level nested struct should round-trip");
1897 assert_eq!(leaf, "value");
1898 }
1899
1900 #[test]
1901 fn struct_member_with_escaped_text_round_trips() {
1902 static VALUE: Schema<'static> =
1903 Schema::new_member(shape_id!("t", "Body"), ShapeType::String, "value", 0);
1904 static BODY_SCHEMA: Schema<'static> =
1905 Schema::new_struct(shape_id!("t", "Body"), ShapeType::Structure, &[&VALUE]);
1906 static PAYLOAD_MEMBER: Schema<'static> = Schema::new_member(
1907 shape_id!("t", "Envelope"),
1908 ShapeType::Structure,
1909 "payload",
1910 0,
1911 );
1912 static ENVELOPE_SCHEMA: Schema<'static> = Schema::new_struct(
1913 shape_id!("t", "Envelope"),
1914 ShapeType::Structure,
1915 &[&PAYLOAD_MEMBER],
1916 );
1917
1918 let xml = b"<Envelope><payload><value>foo&bar</value></payload></Envelope>";
1923 let settings = Arc::new(XmlCodecSettings::default());
1924 let mut deser = XmlDeserializer::new(xml, settings);
1925 let mut value_str = String::new();
1926 deser
1927 .read_struct(&ENVELOPE_SCHEMA, &mut |payload_m, d_payload| {
1928 let _ = payload_m;
1929 d_payload.read_struct(&BODY_SCHEMA, &mut |inner_m, d_inner| {
1930 if inner_m.member_name() == Some("value") {
1931 value_str = d_inner.read_string(inner_m)?;
1932 }
1933 Ok(())
1934 })
1935 })
1936 .expect("escaped text inside a nested struct should round-trip");
1937 assert_eq!(value_str, "foo&bar");
1938 }
1939
1940 #[test]
1941 fn read_map_preserves_empty_string_key() {
1942 static KEY: Schema<'static> =
1945 Schema::new_member(shape_id!("t", "M$key"), ShapeType::String, "key", 0);
1946 static VALUE: Schema<'static> =
1947 Schema::new_member(shape_id!("t", "M$value"), ShapeType::String, "value", 1);
1948 static MAP: Schema<'static> = Schema::new_map(shape_id!("t", "M"), &KEY, &VALUE);
1949
1950 let xml = b"<Root><entry><key></key><value>v1</value></entry></Root>";
1951 let settings = Arc::new(XmlCodecSettings::default());
1952 let mut deser = XmlDeserializer::new(xml, settings);
1953
1954 let mut got: std::collections::HashMap<String, String> = Default::default();
1955 deser
1956 .read_map(&MAP, &mut |k, d| {
1957 let v = d.read_string(&VALUE)?;
1958 got.insert(k, v);
1959 Ok(())
1960 })
1961 .expect("empty-key entry should be preserved");
1962 assert_eq!(got.get("").map(String::as_str), Some("v1"));
1963 }
1964
1965 #[test]
1971 fn read_map_value_is_inner_map_with_renamed_inner_key_value() {
1972 let xml = b"<outerMap><entry><key>ok</key><value><entry><InnerKey>ik</InnerKey><InnerVal>iv</InnerVal></entry></value></entry></outerMap>";
1973 let settings = Arc::new(XmlCodecSettings::default());
1974 let mut deser = XmlDeserializer::new(xml, settings);
1975
1976 static INNER_KEY: Schema<'static> = Schema::new_member(
1977 shape_id!("test", "InnerMap$key"),
1978 ShapeType::String,
1979 "key",
1980 0,
1981 )
1982 .with_xml_name("InnerKey");
1983 static INNER_VAL: Schema<'static> = Schema::new_member(
1984 shape_id!("test", "InnerMap$value"),
1985 ShapeType::String,
1986 "value",
1987 1,
1988 )
1989 .with_xml_name("InnerVal");
1990
1991 static OUTER_VALUE: Schema<'static> = Schema::new_member(
1995 shape_id!("test", "OuterMap$value"),
1996 ShapeType::Map,
1997 "value",
1998 1,
1999 )
2000 .with_map_members(&INNER_KEY, &INNER_VAL);
2001
2002 static OUTER_KEY: Schema<'static> = Schema::new_member(
2003 shape_id!("test", "OuterMap$key"),
2004 ShapeType::String,
2005 "key",
2006 0,
2007 );
2008 static OUTER_MAP_SCHEMA: Schema<'static> =
2009 Schema::new_map(shape_id!("test", "OuterMap"), &OUTER_KEY, &OUTER_VALUE);
2010
2011 let mut got: Vec<(String, Vec<(String, String)>)> = Vec::new();
2012 deser
2013 .read_map(&OUTER_MAP_SCHEMA, &mut |outer_key, d| {
2014 let mut inner: Vec<(String, String)> = Vec::new();
2015 d.read_map(&OUTER_VALUE, &mut |inner_key, d2| {
2016 let inner_val = d2.read_string(&INNER_VAL)?;
2017 inner.push((inner_key, inner_val));
2018 Ok(())
2019 })?;
2020 got.push((outer_key, inner));
2021 Ok(())
2022 })
2023 .expect("nested map deserialization should succeed");
2024
2025 assert_eq!(
2026 got,
2027 vec![("ok".to_owned(), vec![("ik".to_owned(), "iv".to_owned())])]
2028 );
2029 }
2030
2031 #[test]
2036 fn read_list_member_is_inner_map_with_renamed_inner_key_value() {
2037 let xml = b"<items><member><entry><Attr>k1</Attr><Set>v1</Set></entry></member></items>";
2038 let settings = Arc::new(XmlCodecSettings::default());
2039 let mut deser = XmlDeserializer::new(xml, settings);
2040
2041 static INNER_KEY: Schema<'static> = Schema::new_member(
2042 shape_id!("test", "InnerMap$key"),
2043 ShapeType::String,
2044 "key",
2045 0,
2046 )
2047 .with_xml_name("Attr");
2048 static INNER_VAL: Schema<'static> = Schema::new_member(
2049 shape_id!("test", "InnerMap$value"),
2050 ShapeType::String,
2051 "value",
2052 1,
2053 )
2054 .with_xml_name("Set");
2055
2056 static LIST_ITEM: Schema<'static> = Schema::new_member(
2059 shape_id!("test", "OuterList$member"),
2060 ShapeType::Map,
2061 "member",
2062 0,
2063 )
2064 .with_map_members(&INNER_KEY, &INNER_VAL);
2065
2066 static OUTER_LIST_SCHEMA: Schema<'static> =
2067 Schema::new_list(shape_id!("test", "OuterList"), &LIST_ITEM);
2068
2069 let mut got: Vec<Vec<(String, String)>> = Vec::new();
2070 deser
2071 .read_list(&OUTER_LIST_SCHEMA, &mut |d| {
2072 let mut entries: Vec<(String, String)> = Vec::new();
2073 d.read_map(&LIST_ITEM, &mut |inner_key, d2| {
2074 let inner_val = d2.read_string(&INNER_VAL)?;
2075 entries.push((inner_key, inner_val));
2076 Ok(())
2077 })?;
2078 got.push(entries);
2079 Ok(())
2080 })
2081 .expect("list-of-map deserialization should succeed");
2082
2083 assert_eq!(got, vec![vec![("k1".to_owned(), "v1".to_owned())]]);
2084 }
2085}
2086
2087#[cfg(test)]
2094mod skip_value_contract {
2095 use super::*;
2096 use aws_smithy_schema::serde::ShapeDeserializer;
2097 use aws_smithy_schema::{shape_id, Schema, ShapeType};
2098
2099 static BOUND: Schema<'static> = Schema::new_member(
2100 shape_id!("test", "Output$bound"),
2101 ShapeType::String,
2102 "bound",
2103 0,
2104 );
2105 static BODY: Schema<'static> = Schema::new_member(
2106 shape_id!("test", "Output$body"),
2107 ShapeType::String,
2108 "body",
2109 1,
2110 );
2111 static TAIL: Schema<'static> = Schema::new_member(
2112 shape_id!("test", "Output$tail"),
2113 ShapeType::String,
2114 "tail",
2115 2,
2116 );
2117 static OUTPUT: Schema<'static> = Schema::new_struct(
2118 shape_id!("test", "Output"),
2119 ShapeType::Structure,
2120 &[&BOUND, &BODY, &TAIL],
2121 );
2122
2123 fn skip_bound(xml: &[u8]) -> Result<(Option<String>, Option<String>), SerdeError> {
2125 let mut deser = XmlDeserializer::new(xml, Arc::new(XmlCodecSettings::default()));
2126 let (mut body, mut tail) = (None, None);
2127 deser.read_struct(&OUTPUT, &mut |member, d| {
2128 match member.member_index() {
2129 Some(0) => d.skip_value()?,
2130 Some(1) => body = Some(d.read_string(member)?),
2131 Some(2) => tail = Some(d.read_string(member)?),
2132 _ => {}
2133 }
2134 Ok(())
2135 })?;
2136 Ok((body, tail))
2137 }
2138
2139 #[test]
2140 fn skipping_a_member_does_not_consume_its_siblings() {
2141 let xml = b"<Output><bound>skipped</bound><body>real</body><tail>end</tail></Output>";
2144 let (body, tail) = skip_bound(xml).unwrap();
2145 assert_eq!(body.as_deref(), Some("real"));
2146 assert_eq!(tail.as_deref(), Some("end"));
2147 }
2148
2149 #[test]
2150 fn skipping_works_regardless_of_element_order() {
2151 for xml in [
2152 &b"<Output><bound>s</bound><body>real</body><tail>end</tail></Output>"[..],
2153 &b"<Output><body>real</body><bound>s</bound><tail>end</tail></Output>"[..],
2154 &b"<Output><body>real</body><tail>end</tail><bound>s</bound></Output>"[..],
2155 ] {
2156 let (body, tail) = skip_bound(xml).unwrap();
2157 assert_eq!(
2158 (body.as_deref(), tail.as_deref()),
2159 (Some("real"), Some("end")),
2160 "failed for {}",
2161 String::from_utf8_lossy(xml)
2162 );
2163 }
2164 }
2165
2166 #[test]
2167 fn skipping_an_aggregate_member_with_nested_content_does_not_consume_siblings() {
2168 static AGG_BOUND: Schema<'static> = Schema::new_member(
2177 shape_id!("test", "Output$bound"),
2178 ShapeType::Structure,
2179 "bound",
2180 0,
2181 );
2182 static AGG_OUTPUT: Schema<'static> = Schema::new_struct(
2183 shape_id!("test", "Output"),
2184 ShapeType::Structure,
2185 &[&AGG_BOUND, &BODY, &TAIL],
2186 );
2187
2188 let xml = b"<Output><bound><inner>x</inner><body>decoy</body></bound>\
2192 <body>real</body><tail>end</tail></Output>";
2193 let mut deser = XmlDeserializer::new(xml, Arc::new(XmlCodecSettings::default()));
2194 let (mut body, mut tail) = (None, None);
2195 deser
2196 .read_struct(&AGG_OUTPUT, &mut |member, d| {
2197 match member.member_index() {
2198 Some(0) => d.skip_value()?,
2199 Some(1) => body = Some(d.read_string(member)?),
2200 Some(2) => tail = Some(d.read_string(member)?),
2201 _ => {}
2202 }
2203 Ok(())
2204 })
2205 .unwrap();
2206 assert_eq!(body.as_deref(), Some("real"));
2207 assert_eq!(tail.as_deref(), Some("end"));
2208 }
2209
2210 #[test]
2211 fn skipping_a_self_closing_and_empty_element_is_fine() {
2212 for xml in [
2213 &b"<Output><bound/><body>real</body><tail>end</tail></Output>"[..],
2214 &b"<Output><bound></bound><body>real</body><tail>end</tail></Output>"[..],
2215 ] {
2216 let (body, tail) = skip_bound(xml).unwrap();
2217 assert_eq!(
2218 (body.as_deref(), tail.as_deref()),
2219 (Some("real"), Some("end")),
2220 "failed for {}",
2221 String::from_utf8_lossy(xml)
2222 );
2223 }
2224 }
2225
2226 #[test]
2227 fn skip_value_is_a_no_op_on_a_text_deserializer() {
2228 static V: Schema<'static> =
2232 Schema::new_member(shape_id!("test", "S$v"), ShapeType::String, "v", 0);
2233 let mut deser =
2234 XmlDeserializer::new(b"<v>hello</v>", Arc::new(XmlCodecSettings::default()));
2235 let dynamic: &mut dyn ShapeDeserializer = &mut deser;
2236 dynamic.skip_value().unwrap();
2237 assert_eq!(deser.read_string(&V).unwrap(), "hello");
2239 }
2240}