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>> {
157 schema.members().iter().copied().find(|m| {
158 if let Some(xml_name) = m.xml_name() {
159 xml_name.value() == element_name
160 } else {
161 m.member_name() == Some(element_name)
162 }
163 })
164 }
165
166 pub(crate) fn find_element_slice(input: &'a [u8], el_local: &str) -> &'a [u8] {
180 debug_assert!(
188 {
189 let lo = input.as_ptr() as usize;
190 let hi = lo + input.len();
191 let p = el_local.as_ptr() as usize;
192 p >= lo && p + el_local.len() <= hi
193 },
194 "find_element_slice: el_local must point into input"
195 );
196 let name_ptr = el_local.as_ptr() as usize;
197 let input_start = input.as_ptr() as usize;
198 let name_offset = name_ptr.saturating_sub(input_start).min(input.len());
199
200 let el_start = input[..name_offset]
203 .iter()
204 .rposition(|&b| b == b'<')
205 .unwrap_or(0);
206
207 let tag_name = el_local.as_bytes();
211 let remaining = &input[el_start..];
212 let mut depth = 0i32;
213 let mut pos = 0;
214 while pos < remaining.len() {
215 if remaining[pos..].starts_with(b"</") {
216 let after_slash = pos + 2;
218 if remaining[after_slash..].starts_with(tag_name) {
219 let after_name = after_slash + tag_name.len();
220 if remaining.get(after_name) == Some(&b'>') {
221 depth -= 1;
222 if depth == 0 {
223 let end = el_start + after_name + 1;
224 return &input[el_start..end];
225 }
226 }
227 }
228 pos = after_slash;
229 } else if remaining[pos] == b'<'
230 && remaining.get(pos + 1) != Some(&b'/')
231 && remaining.get(pos + 1) != Some(&b'?')
232 {
233 if let Some(gt) = remaining[pos..].iter().position(|&b| b == b'>') {
235 let tag_content = &remaining[pos + 1..pos + gt];
236 let is_self_closing = tag_content.last() == Some(&b'/');
237 let opens_our_tag = tag_content.starts_with(tag_name)
238 && tag_content
239 .get(tag_name.len())
240 .is_none_or(|&b| b == b' ' || b == b'>' || b == b'/');
241 if opens_our_tag && is_self_closing && depth == 0 {
242 let end = el_start + pos + gt + 1;
246 return &input[el_start..end];
247 }
248 if opens_our_tag && !is_self_closing {
249 depth += 1;
250 }
251 pos += gt + 1;
252 } else {
253 pos += 1;
254 }
255 } else {
256 pos += 1;
262 }
263 }
264 &input[el_start..]
266 }
267
268 fn resolve_timestamp_format(&self, schema: &Schema<'_>) -> TimestampFormat {
269 schema
270 .timestamp_format()
271 .map(|t| match t.format() {
272 aws_smithy_schema::traits::TimestampFormat::EpochSeconds => {
273 TimestampFormat::EpochSeconds
274 }
275 aws_smithy_schema::traits::TimestampFormat::DateTime => {
280 TimestampFormat::DateTimeWithOffset
281 }
282 aws_smithy_schema::traits::TimestampFormat::HttpDate => TimestampFormat::HttpDate,
283 })
284 .unwrap_or_else(|| match self.settings.default_timestamp_format() {
285 TimestampFormat::DateTime => TimestampFormat::DateTimeWithOffset,
286 other => other,
287 })
288 }
289}
290
291pub fn find_depth2_element_slice_by(
305 body: &[u8],
306 predicate: impl Fn(&str) -> bool,
307) -> Option<&[u8]> {
308 let mut doc = Document::try_from(body).ok()?;
309 let mut root = doc.root_element().ok()?;
310 if predicate(root.start_el().local()) {
313 return Some(body);
314 }
315 while let Some(tag) = root.next_tag() {
317 let local = tag.start_el().local();
318 if predicate(local) {
319 return Some(XmlDeserializer::find_element_slice(body, local));
322 }
323 }
324 None
325}
326
327impl ShapeDeserializer for XmlDeserializer<'_> {
328 fn read_struct(
329 &mut self,
330 schema: &Schema<'_>,
331 consumer: &mut dyn FnMut(&Schema<'_>, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
332 ) -> Result<(), SerdeError> {
333 self.enter_aggregate()?;
334 let result = (|| -> Result<(), SerdeError> {
340 let input = self.input;
346 let mut doc = self.document()?;
347 let mut root = doc
348 .root_element()
349 .map_err(|e| SerdeError::custom(e.to_string()))?;
350
351 if self.settings.enforce_strictness && self.depth == 1 {
352 let expected = schema
353 .xml_name()
354 .map(|t| t.value())
355 .or_else(|| schema.original_name())
356 .or_else(|| schema.member_name())
357 .unwrap_or_else(|| schema.shape_id().shape_name());
358 if !root.start_el().matches(expected) {
359 return Err(SerdeError::invalid_input(format!(
360 "expected XML root {expected}"
361 )));
362 }
363 }
364
365 if schema.xml_unwrapped_output() {
374 let el_local = root.start_el().local();
386 let sub = Self::find_element_slice(input, el_local);
387 let local = el_local.to_owned();
388 drop(root);
394 let _ = doc;
395 if let Some(member) = Self::resolve_member(schema, &local) {
396 self.dispatch_subslice(sub, |this| consumer(member, this))?;
397 }
398 return Ok(());
399 }
400
401 for member in schema.members() {
403 if member.xml_attribute() {
404 let attr_name = member
405 .xml_name()
406 .map(|t| t.value())
407 .or(member.member_name())
408 .unwrap_or("");
409 if let Some(value) = root.start_el().attr(attr_name) {
410 let text = Cow::Owned(value.to_owned());
411 self.dispatch_text(text, |this| consumer(member, this))?;
412 }
413 }
414 }
415
416 let mut flattened_groups: std::collections::HashMap<usize, (&Schema<'_>, Vec<u8>)> =
420 std::collections::HashMap::new();
421
422 while let Some(mut child_scope) = root.next_tag() {
424 let local = child_scope.start_el().local().to_owned();
425 let Some(member) = Self::resolve_member(schema, &local) else {
426 continue;
427 };
428 let is_aggregate = member.shape_type().is_aggregate();
436 if is_aggregate && !member.xml_flattened() {
437 let el_local = child_scope.start_el().local();
438 let sub = Self::find_element_slice(input, el_local);
439 drop(child_scope);
440 self.dispatch_subslice(sub, |this| consumer(member, this))?;
441 } else if is_aggregate {
442 let el_local = child_scope.start_el().local();
445 let sub = Self::find_element_slice(input, el_local);
446 drop(child_scope);
447 let idx = member.member_index().unwrap_or(usize::MAX);
448 let entry = flattened_groups
449 .entry(idx)
450 .or_insert_with(|| (member, Vec::new()));
451 entry.1.extend_from_slice(sub);
452 } else {
453 let text = decode::try_data(&mut child_scope)
454 .map_err(|e| SerdeError::custom(e.to_string()))?;
455 drop(child_scope);
456 self.dispatch_text(text, |this| consumer(member, this))?;
457 }
458 }
459
460 for (_idx, (member, bytes)) in flattened_groups {
468 let mut wrapped = Vec::with_capacity(bytes.len() + 16);
469 wrapped.extend_from_slice(b"<__flat>");
470 wrapped.extend_from_slice(&bytes);
471 wrapped.extend_from_slice(b"</__flat>");
472 let mut child_deser = XmlDeserializer::new(&wrapped, self.settings.clone());
473 consumer(member, &mut child_deser)?;
474 }
475 Ok(())
476 })();
477 self.leave_aggregate();
478 result
479 }
480
481 fn read_list(
482 &mut self,
483 _schema: &Schema<'_>,
484 consumer: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
485 ) -> Result<(), SerdeError> {
486 self.enter_aggregate()?;
487 let result = (|| -> Result<(), SerdeError> {
490 let input = self.input;
491 let mut doc = self.document()?;
492 let mut root = doc
493 .root_element()
494 .map_err(|e| SerdeError::custom(e.to_string()))?;
495
496 while let Some(child_scope) = root.next_tag() {
505 let el_local = child_scope.start_el().local();
506 let sub = Self::find_element_slice(input, el_local);
507 drop(child_scope);
508 self.dispatch_subslice(sub, |this| consumer(this))?;
509 }
510 Ok(())
511 })();
512 self.leave_aggregate();
513 result
514 }
515
516 fn read_map(
517 &mut self,
518 schema: &Schema<'_>,
519 consumer: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
520 ) -> Result<(), SerdeError> {
521 self.enter_aggregate()?;
522 let result = (|| -> Result<(), SerdeError> {
525 let input = self.input;
526 let mut doc = self.document()?;
527 let mut root = doc
528 .root_element()
529 .map_err(|e| SerdeError::custom(e.to_string()))?;
530
531 let key_name = schema
533 .key()
534 .and_then(|k| k.xml_name().map(|t| t.value()))
535 .unwrap_or("key");
536 let value_name = schema
537 .member()
538 .and_then(|v| v.xml_name().map(|t| t.value()))
539 .unwrap_or("value");
540
541 let value_is_aggregate = schema
544 .member()
545 .map(|v| v.shape_type().is_aggregate())
546 .unwrap_or(false);
547
548 while let Some(mut entry_scope) = root.next_tag() {
550 let mut key: Option<String> = None;
551 let mut value_text: Option<Cow<'_, str>> = None;
554 let mut value_slice: Option<&'_ [u8]> = None;
555 while let Some(mut field_scope) = entry_scope.next_tag() {
556 let local = field_scope.start_el().local().to_owned();
557 if local == key_name {
558 let text = decode::try_data(&mut field_scope)
559 .map_err(|e| SerdeError::custom(e.to_string()))?;
560 key = Some(text.into_owned());
561 } else if local == value_name {
562 if value_is_aggregate {
563 let el_local = field_scope.start_el().local();
564 let sub = Self::find_element_slice(input, el_local);
565 drop(field_scope);
566 value_slice = Some(sub);
567 } else {
568 let text = decode::try_data(&mut field_scope)
569 .map_err(|e| SerdeError::custom(e.to_string()))?;
570 value_text = Some(text);
571 }
572 }
573 }
574 drop(entry_scope);
575 if let Some(k) = key {
576 if let Some(slice) = value_slice {
577 self.dispatch_subslice(slice, |this| consumer(k, this))?;
578 } else if let Some(t) = value_text {
579 let t: Cow<'_, str> = t;
583 self.dispatch_text(t.into_owned().into(), |this| consumer(k, this))?;
584 }
585 }
586 }
587 Ok(())
588 })();
589 self.leave_aggregate();
590 result
591 }
592
593 fn read_boolean(&mut self, _schema: &Schema<'_>) -> Result<bool, SerdeError> {
594 let text = self.take_text()?;
595 match text.as_ref() {
596 "true" => Ok(true),
597 "false" => Ok(false),
598 other => Err(SerdeError::custom(format!("invalid boolean: {other}"))),
599 }
600 }
601
602 fn read_byte(&mut self, _schema: &Schema<'_>) -> Result<i8, SerdeError> {
603 let text = self.take_text()?;
604 text.parse().map_err(|e| SerdeError::custom(format!("{e}")))
605 }
606
607 fn read_short(&mut self, _schema: &Schema<'_>) -> Result<i16, SerdeError> {
608 let text = self.take_text()?;
609 text.parse().map_err(|e| SerdeError::custom(format!("{e}")))
610 }
611
612 fn read_integer(&mut self, _schema: &Schema<'_>) -> Result<i32, SerdeError> {
613 let text = self.take_text()?;
614 text.parse().map_err(|e| SerdeError::custom(format!("{e}")))
615 }
616
617 fn read_long(&mut self, _schema: &Schema<'_>) -> Result<i64, SerdeError> {
618 let text = self.take_text()?;
619 text.parse().map_err(|e| SerdeError::custom(format!("{e}")))
620 }
621
622 fn read_float(&mut self, _schema: &Schema<'_>) -> Result<f32, SerdeError> {
623 let text = self.take_text()?;
624 match text.as_ref() {
625 "NaN" => Ok(f32::NAN),
626 "Infinity" => Ok(f32::INFINITY),
627 "-Infinity" => Ok(f32::NEG_INFINITY),
628 _ => text.parse().map_err(|e| SerdeError::custom(format!("{e}"))),
629 }
630 }
631
632 fn read_double(&mut self, _schema: &Schema<'_>) -> Result<f64, SerdeError> {
633 let text = self.take_text()?;
634 match text.as_ref() {
635 "NaN" => Ok(f64::NAN),
636 "Infinity" => Ok(f64::INFINITY),
637 "-Infinity" => Ok(f64::NEG_INFINITY),
638 _ => text.parse().map_err(|e| SerdeError::custom(format!("{e}"))),
639 }
640 }
641
642 fn read_big_integer(&mut self, _schema: &Schema<'_>) -> Result<BigInteger, SerdeError> {
643 let text = self.take_text()?;
644 text.parse().map_err(|e| SerdeError::custom(format!("{e}")))
645 }
646
647 fn read_big_decimal(&mut self, _schema: &Schema<'_>) -> Result<BigDecimal, SerdeError> {
648 let text = self.take_text()?;
649 text.parse().map_err(|e| SerdeError::custom(format!("{e}")))
650 }
651
652 fn read_string(&mut self, _schema: &Schema<'_>) -> Result<String, SerdeError> {
653 let text = self.take_text()?;
654 Ok(text.into_owned())
655 }
656
657 fn read_blob(&mut self, _schema: &Schema<'_>) -> Result<Blob, SerdeError> {
658 let text = self.take_text()?;
659 let bytes = aws_smithy_types::base64::decode(text.as_ref())
660 .map_err(|e| SerdeError::custom(format!("{e}")))?;
661 Ok(Blob::new(bytes))
662 }
663
664 fn read_timestamp(&mut self, schema: &Schema<'_>) -> Result<DateTime, SerdeError> {
665 let text = self.take_text()?;
666 let format = self.resolve_timestamp_format(schema);
667 DateTime::from_str(text.as_ref(), format).map_err(|e| SerdeError::custom(format!("{e}")))
668 }
669
670 fn read_document(&mut self, _schema: &Schema<'_>) -> Result<SmithyDocument, SerdeError> {
671 Err(SerdeError::custom(
672 "document types are not supported by REST XML",
673 ))
674 }
675
676 fn read_string_list(&mut self, _schema: &Schema<'_>) -> Result<Vec<String>, SerdeError> {
703 self.enter_aggregate()?;
704 let result = (|| -> Result<Vec<String>, SerdeError> {
709 let mut doc = self.document()?;
710 let mut root = doc
711 .root_element()
712 .map_err(|e| SerdeError::custom(e.to_string()))?;
713 let mut out = Vec::new();
714 while let Some(mut child_scope) = root.next_tag() {
715 let text = decode::try_data(&mut child_scope)
716 .map_err(|e| SerdeError::custom(e.to_string()))?;
717 out.push(text.into_owned());
718 }
719 Ok(out)
720 })();
721 self.leave_aggregate();
722 result
723 }
724
725 fn read_blob_list(&mut self, _schema: &Schema<'_>) -> Result<Vec<Blob>, SerdeError> {
726 use aws_smithy_types::base64;
727 self.enter_aggregate()?;
728 let result = (|| -> Result<Vec<Blob>, SerdeError> {
729 let mut doc = self.document()?;
730 let mut root = doc
731 .root_element()
732 .map_err(|e| SerdeError::custom(e.to_string()))?;
733 let mut out = Vec::new();
734 while let Some(mut child_scope) = root.next_tag() {
735 let text = decode::try_data(&mut child_scope)
736 .map_err(|e| SerdeError::custom(e.to_string()))?;
737 let bytes = base64::decode(text.as_ref())
738 .map_err(|e| SerdeError::custom(format!("invalid base64: {e}")))?;
739 out.push(Blob::new(bytes));
740 }
741 Ok(out)
742 })();
743 self.leave_aggregate();
744 result
745 }
746
747 fn read_integer_list(&mut self, _schema: &Schema<'_>) -> Result<Vec<i32>, SerdeError> {
748 self.enter_aggregate()?;
749 let result = (|| -> Result<Vec<i32>, SerdeError> {
750 let mut doc = self.document()?;
751 let mut root = doc
752 .root_element()
753 .map_err(|e| SerdeError::custom(e.to_string()))?;
754 let mut out = Vec::new();
755 while let Some(mut child_scope) = root.next_tag() {
756 let text = decode::try_data(&mut child_scope)
757 .map_err(|e| SerdeError::custom(e.to_string()))?;
758 let v: i32 = text
759 .parse()
760 .map_err(|e| SerdeError::custom(format!("{e}")))?;
761 out.push(v);
762 }
763 Ok(out)
764 })();
765 self.leave_aggregate();
766 result
767 }
768
769 fn read_long_list(&mut self, _schema: &Schema<'_>) -> Result<Vec<i64>, SerdeError> {
770 self.enter_aggregate()?;
771 let result = (|| -> Result<Vec<i64>, SerdeError> {
772 let mut doc = self.document()?;
773 let mut root = doc
774 .root_element()
775 .map_err(|e| SerdeError::custom(e.to_string()))?;
776 let mut out = Vec::new();
777 while let Some(mut child_scope) = root.next_tag() {
778 let text = decode::try_data(&mut child_scope)
779 .map_err(|e| SerdeError::custom(e.to_string()))?;
780 let v: i64 = text
781 .parse()
782 .map_err(|e| SerdeError::custom(format!("{e}")))?;
783 out.push(v);
784 }
785 Ok(out)
786 })();
787 self.leave_aggregate();
788 result
789 }
790
791 fn read_string_string_map(
792 &mut self,
793 schema: &Schema<'_>,
794 ) -> Result<std::collections::HashMap<String, String>, SerdeError> {
795 let key_name = schema
796 .key()
797 .and_then(|k| k.xml_name().map(|t| t.value()))
798 .unwrap_or("key");
799 let value_name = schema
800 .member()
801 .and_then(|v| v.xml_name().map(|t| t.value()))
802 .unwrap_or("value");
803
804 self.enter_aggregate()?;
805 let result = (|| -> Result<std::collections::HashMap<String, String>, SerdeError> {
806 let mut doc = self.document()?;
807 let mut root = doc
808 .root_element()
809 .map_err(|e| SerdeError::custom(e.to_string()))?;
810 let mut out = std::collections::HashMap::new();
811 while let Some(mut entry_scope) = root.next_tag() {
812 let mut k: Option<String> = None;
813 let mut v: Option<String> = None;
814 while let Some(mut field_scope) = entry_scope.next_tag() {
815 let local = field_scope.start_el().local().to_owned();
816 if local == key_name {
817 let text = decode::try_data(&mut field_scope)
818 .map_err(|e| SerdeError::custom(e.to_string()))?;
819 k = Some(text.into_owned());
820 } else if local == value_name {
821 let text = decode::try_data(&mut field_scope)
822 .map_err(|e| SerdeError::custom(e.to_string()))?;
823 v = Some(text.into_owned());
824 }
825 }
826 if let (Some(k), Some(v)) = (k, v) {
827 out.insert(k, v);
828 }
829 }
830 Ok(out)
831 })();
832 self.leave_aggregate();
833 result
834 }
835
836 fn is_null(&self) -> bool {
837 false
840 }
841
842 fn container_size(&self) -> Option<usize> {
843 None
844 }
845}
846
847#[cfg(test)]
848mod tests {
849 use super::*;
850 use aws_smithy_schema::{shape_id, Schema, ShapeType};
851
852 static STRING_MEMBER: Schema<'static> =
853 Schema::new_member(shape_id!("test", "S$v"), ShapeType::String, "v", 0);
854
855 #[test]
856 fn strict_roots_only_validate_document_boundary() {
857 static CHILD: Schema<'static> = Schema::new_struct(
858 aws_smithy_schema::shape_id!("test", "Child"),
859 aws_smithy_schema::ShapeType::Structure,
860 &[],
861 );
862 static RENAMED: Schema<'static> = Schema::new_member(
863 aws_smithy_schema::shape_id!("test", "Root", "child"),
864 aws_smithy_schema::ShapeType::Structure,
865 "child",
866 0,
867 )
868 .with_xml_name("Renamed");
869 static ROOT: Schema<'static> = Schema::new_struct(
870 aws_smithy_schema::shape_id!("test", "Synthetic"),
871 aws_smithy_schema::ShapeType::Structure,
872 &[&RENAMED],
873 )
874 .with_original_name("Original")
875 .with_xml_name("WireRoot");
876 for strict in [false, true] {
877 let settings = Arc::new(
878 XmlCodecSettings::builder()
879 .enforce_strictness(strict)
880 .build(),
881 );
882 for (input, matches) in [
883 (b"<WireRoot><Renamed/></WireRoot>".as_slice(), true),
884 (b"<Wrong><Renamed/></Wrong>", false),
885 ] {
886 let mut visited = false;
887 let result = XmlDeserializer::new(input, settings.clone()).read_struct(
888 &ROOT,
889 &mut |_, d| {
890 visited = true;
891 d.read_struct(&CHILD, &mut |_, _| Ok(()))
892 },
893 );
894 assert_eq!(result.is_ok(), !strict || matches);
895 assert_eq!(visited, !strict || matches);
896 }
897 }
898 }
899
900 #[test]
901 fn read_string_from_text_state() {
902 let settings = Arc::new(XmlCodecSettings::default());
903 let mut deser = XmlDeserializer::from_text(Cow::Borrowed("hello"), settings);
904 let result = deser.read_string(&STRING_MEMBER).unwrap();
905 assert_eq!(result, "hello");
906 }
907
908 #[test]
909 fn read_string_from_doc_text_content() {
910 static V_MEMBER: Schema<'static> =
914 Schema::new_member(shape_id!("test", "X$v"), ShapeType::String, "v", 0);
915 let xml = b"<root>world</root>";
916 let settings = Arc::new(XmlCodecSettings::default());
917 let mut deser = XmlDeserializer::new(xml, settings);
918 let result = deser.read_string(&V_MEMBER).unwrap();
919 assert_eq!(result, "world");
920 }
921
922 #[test]
923 fn is_null_always_false() {
924 let settings = Arc::new(XmlCodecSettings::default());
925 let deser = XmlDeserializer::new(b"<r/>", settings);
926 assert!(!deser.is_null());
927 }
928
929 static NAME_MEMBER: Schema<'static> = Schema::new_member(
932 shape_id!("test", "Person$name"),
933 ShapeType::String,
934 "name",
935 0,
936 );
937 static AGE_MEMBER: Schema<'static> =
938 Schema::new_member(shape_id!("test", "Person$age"), ShapeType::String, "age", 1);
939 static RENAMED_MEMBER: Schema<'static> = Schema::new_member(
940 shape_id!("test", "Person$nick"),
941 ShapeType::String,
942 "nick",
943 2,
944 )
945 .with_xml_name("Nickname");
946
947 static PERSON_SCHEMA: Schema<'static> = Schema::new_struct(
948 shape_id!("test", "Person"),
949 ShapeType::Structure,
950 &[&NAME_MEMBER, &AGE_MEMBER, &RENAMED_MEMBER],
951 );
952
953 #[test]
954 fn read_struct_dispatches_members() {
955 let xml = b"<Person><name>Alice</name><age>30</age></Person>";
956 let settings = Arc::new(XmlCodecSettings::default());
957 let mut deser = XmlDeserializer::new(xml, settings);
958
959 let mut name = String::new();
960 let mut age = String::new();
961 deser
962 .read_struct(&PERSON_SCHEMA, &mut |member, d| {
963 match member.member_name().unwrap() {
964 "name" => name = d.read_string(member)?,
965 "age" => age = d.read_string(member)?,
966 _ => {}
967 }
968 Ok(())
969 })
970 .unwrap();
971
972 assert_eq!(name, "Alice");
973 assert_eq!(age, "30");
974 }
975
976 #[test]
977 fn read_struct_skips_unknown_elements() {
978 let xml = b"<Person><unknown>x</unknown><name>Bob</name></Person>";
979 let settings = Arc::new(XmlCodecSettings::default());
980 let mut deser = XmlDeserializer::new(xml, settings);
981
982 let mut name = String::new();
983 deser
984 .read_struct(&PERSON_SCHEMA, &mut |member, d| {
985 if member.member_name() == Some("name") {
986 name = d.read_string(member)?;
987 }
988 Ok(())
989 })
990 .unwrap();
991
992 assert_eq!(name, "Bob");
993 }
994
995 #[test]
996 fn read_struct_resolves_xml_name() {
997 let xml = b"<Person><Nickname>Ally</Nickname></Person>";
998 let settings = Arc::new(XmlCodecSettings::default());
999 let mut deser = XmlDeserializer::new(xml, settings);
1000
1001 let mut nick = String::new();
1002 deser
1003 .read_struct(&PERSON_SCHEMA, &mut |member, d| {
1004 if member.member_name() == Some("nick") {
1005 nick = d.read_string(member)?;
1006 }
1007 Ok(())
1008 })
1009 .unwrap();
1010
1011 assert_eq!(nick, "Ally");
1012 }
1013
1014 static ATTR_MEMBER: Schema<'static> =
1017 Schema::new_member(shape_id!("test", "X$id"), ShapeType::String, "id", 0)
1018 .with_xml_attribute();
1019 static ELEM_MEMBER: Schema<'static> =
1020 Schema::new_member(shape_id!("test", "X$name"), ShapeType::String, "name", 1);
1021
1022 static X_SCHEMA: Schema<'static> = Schema::new_struct(
1023 shape_id!("test", "X"),
1024 ShapeType::Structure,
1025 &[&ATTR_MEMBER, &ELEM_MEMBER],
1026 );
1027
1028 #[test]
1029 fn read_struct_dispatches_attributes() {
1030 let xml = b"<X id=\"42\"><name>hello</name></X>";
1031 let settings = Arc::new(XmlCodecSettings::default());
1032 let mut deser = XmlDeserializer::new(xml, settings);
1033
1034 let mut id = String::new();
1035 let mut name = String::new();
1036 deser
1037 .read_struct(&X_SCHEMA, &mut |member, d| {
1038 match member.member_name().unwrap() {
1039 "id" => id = d.read_string(member)?,
1040 "name" => name = d.read_string(member)?,
1041 _ => {}
1042 }
1043 Ok(())
1044 })
1045 .unwrap();
1046
1047 assert_eq!(id, "42");
1048 assert_eq!(name, "hello");
1049 }
1050
1051 #[test]
1054 fn read_list_wrapped() {
1055 let xml = b"<items><member>a</member><member>b</member></items>";
1056 let settings = Arc::new(XmlCodecSettings::default());
1057 let mut deser = XmlDeserializer::new(xml, settings);
1058
1059 static LIST_MEMBER: Schema<'static> = Schema::new_member(
1060 shape_id!("test", "L$member"),
1061 ShapeType::String,
1062 "member",
1063 0,
1064 );
1065 static LIST_SCHEMA: Schema<'static> =
1066 Schema::new_list(shape_id!("test", "L"), &LIST_MEMBER);
1067
1068 let mut items = Vec::new();
1069 deser
1070 .read_list(&LIST_SCHEMA, &mut |d| {
1071 items.push(d.read_string(&LIST_MEMBER)?);
1072 Ok(())
1073 })
1074 .unwrap();
1075
1076 assert_eq!(items, vec!["a", "b"]);
1077 }
1078
1079 #[test]
1080 fn read_map_wrapped() {
1081 let xml = b"<myMap><entry><key>k1</key><value>v1</value></entry><entry><key>k2</key><value>v2</value></entry></myMap>";
1082 let settings = Arc::new(XmlCodecSettings::default());
1083 let mut deser = XmlDeserializer::new(xml, settings);
1084
1085 static MAP_KEY: Schema<'static> =
1086 Schema::new_member(shape_id!("test", "M$key"), ShapeType::String, "key", 0);
1087 static MAP_VALUE: Schema<'static> =
1088 Schema::new_member(shape_id!("test", "M$value"), ShapeType::String, "value", 0);
1089 static MAP_SCHEMA: Schema<'static> =
1090 Schema::new_map(shape_id!("test", "M"), &MAP_KEY, &MAP_VALUE);
1091
1092 let mut entries = Vec::new();
1093 deser
1094 .read_map(&MAP_SCHEMA, &mut |k, d| {
1095 entries.push((k, d.read_string(&MAP_VALUE)?));
1096 Ok(())
1097 })
1098 .unwrap();
1099
1100 assert_eq!(
1101 entries,
1102 vec![
1103 ("k1".to_owned(), "v1".to_owned()),
1104 ("k2".to_owned(), "v2".to_owned())
1105 ]
1106 );
1107 }
1108
1109 #[test]
1110 fn read_map_with_renamed_key_value() {
1111 let xml = b"<m><entry><Attribute>a</Attribute><Setting>s</Setting></entry></m>";
1112 let settings = Arc::new(XmlCodecSettings::default());
1113 let mut deser = XmlDeserializer::new(xml, settings);
1114
1115 static MAP_KEY: Schema<'static> =
1116 Schema::new_member(shape_id!("test", "M$key"), ShapeType::String, "key", 0)
1117 .with_xml_name("Attribute");
1118 static MAP_VALUE: Schema<'static> =
1119 Schema::new_member(shape_id!("test", "M$value"), ShapeType::String, "value", 0)
1120 .with_xml_name("Setting");
1121 static MAP_SCHEMA: Schema<'static> =
1122 Schema::new_map(shape_id!("test", "M"), &MAP_KEY, &MAP_VALUE);
1123
1124 let mut entries = Vec::new();
1125 deser
1126 .read_map(&MAP_SCHEMA, &mut |k, d| {
1127 entries.push((k, d.read_string(&MAP_VALUE)?));
1128 Ok(())
1129 })
1130 .unwrap();
1131
1132 assert_eq!(entries, vec![("a".to_owned(), "s".to_owned())]);
1133 }
1134
1135 #[test]
1138 fn read_struct_flattened_list() {
1139 let xml = b"<S><name>hi</name><item>a</item><item>b</item></S>";
1141 let settings = Arc::new(XmlCodecSettings::default());
1142 let mut deser = XmlDeserializer::new(xml, settings);
1143
1144 static S_NAME: Schema<'static> =
1145 Schema::new_member(shape_id!("test", "S$name"), ShapeType::String, "name", 0);
1146 static S_ITEMS: Schema<'static> =
1147 Schema::new_member(shape_id!("test", "S$items"), ShapeType::List, "items", 1)
1148 .with_xml_flattened()
1149 .with_xml_name("item");
1150 static S_SCHEMA: Schema<'static> = Schema::new_struct(
1151 shape_id!("test", "S"),
1152 ShapeType::Structure,
1153 &[&S_NAME, &S_ITEMS],
1154 );
1155
1156 let mut name = String::new();
1157 let mut items = Vec::new();
1158 deser
1159 .read_struct(&S_SCHEMA, &mut |member, d| {
1160 match member.member_name().unwrap() {
1161 "name" => name = d.read_string(member)?,
1162 "items" => d.read_list(member, &mut |d| {
1163 items.push(d.read_string(member)?);
1164 Ok(())
1165 })?,
1166 _ => {}
1167 }
1168 Ok(())
1169 })
1170 .unwrap();
1171
1172 assert_eq!(name, "hi");
1173 assert_eq!(items, vec!["a", "b"]);
1174 }
1175
1176 #[test]
1177 fn read_struct_flattened_list_intermixed() {
1178 let xml = b"<S><item>x</item><name>n</name><item>y</item></S>";
1180 let settings = Arc::new(XmlCodecSettings::default());
1181 let mut deser = XmlDeserializer::new(xml, settings);
1182
1183 static S_NAME: Schema<'static> =
1184 Schema::new_member(shape_id!("test", "S$name"), ShapeType::String, "name", 0);
1185 static S_ITEMS: Schema<'static> =
1186 Schema::new_member(shape_id!("test", "S$items"), ShapeType::List, "items", 1)
1187 .with_xml_flattened()
1188 .with_xml_name("item");
1189 static S_SCHEMA: Schema<'static> = Schema::new_struct(
1190 shape_id!("test", "S"),
1191 ShapeType::Structure,
1192 &[&S_NAME, &S_ITEMS],
1193 );
1194
1195 let mut name = String::new();
1196 let mut items = Vec::new();
1197 deser
1198 .read_struct(&S_SCHEMA, &mut |member, d| {
1199 match member.member_name().unwrap() {
1200 "name" => name = d.read_string(member)?,
1201 "items" => d.read_list(member, &mut |d| {
1202 items.push(d.read_string(member)?);
1203 Ok(())
1204 })?,
1205 _ => {}
1206 }
1207 Ok(())
1208 })
1209 .unwrap();
1210
1211 assert_eq!(name, "n");
1212 assert_eq!(items, vec!["x", "y"]);
1213 }
1214
1215 #[test]
1218 fn read_scalars() {
1219 let settings = Arc::new(XmlCodecSettings::default());
1220
1221 let mut d = XmlDeserializer::from_text(Cow::Borrowed("true"), settings.clone());
1222 assert!(d.read_boolean(&STRING_MEMBER).unwrap());
1223
1224 let mut d = XmlDeserializer::from_text(Cow::Borrowed("-42"), settings.clone());
1225 assert_eq!(d.read_integer(&STRING_MEMBER).unwrap(), -42);
1226
1227 let mut d = XmlDeserializer::from_text(Cow::Borrowed("NaN"), settings.clone());
1228 assert!(d.read_float(&STRING_MEMBER).unwrap().is_nan());
1229
1230 let mut d = XmlDeserializer::from_text(Cow::Borrowed("Infinity"), settings.clone());
1231 assert_eq!(d.read_double(&STRING_MEMBER).unwrap(), f64::INFINITY);
1232
1233 let mut d = XmlDeserializer::from_text(Cow::Borrowed("aGVsbG8="), settings.clone());
1234 assert_eq!(d.read_blob(&STRING_MEMBER).unwrap().as_ref(), b"hello");
1235
1236 let mut d =
1237 XmlDeserializer::from_text(Cow::Borrowed("2023-04-01T12:00:00Z"), settings.clone());
1238 let ts = d.read_timestamp(&STRING_MEMBER).unwrap();
1239 assert_eq!(ts.secs(), 1680350400);
1240 }
1241
1242 #[test]
1243 fn read_document_returns_error() {
1244 let settings = Arc::new(XmlCodecSettings::default());
1245 let mut deser = XmlDeserializer::from_text(Cow::Borrowed("x"), settings);
1246 assert_eq!(
1247 deser.read_document(&STRING_MEMBER).unwrap_err().to_string(),
1248 "document types are not supported by REST XML"
1249 );
1250 }
1251
1252 #[test]
1263 fn read_struct_empty_body_errors() {
1264 let settings = Arc::new(XmlCodecSettings::default());
1265 let mut deser = XmlDeserializer::new(b"", settings);
1266 let err = deser
1267 .read_struct(&PERSON_SCHEMA, &mut |_member, _d| Ok(()))
1268 .expect_err("empty body must be rejected by read_struct");
1269 let _ = format!("{err}");
1270 }
1271
1272 #[test]
1273 fn read_list_empty_body_errors() {
1274 let settings = Arc::new(XmlCodecSettings::default());
1275 let mut deser = XmlDeserializer::new(b"", settings);
1276 deser
1277 .read_list(&PERSON_SCHEMA, &mut |_d| Ok(()))
1278 .expect_err("empty body must be rejected by read_list");
1279 }
1280
1281 #[test]
1282 fn read_map_empty_body_errors() {
1283 let settings = Arc::new(XmlCodecSettings::default());
1284 let mut deser = XmlDeserializer::new(b"", settings);
1285 deser
1286 .read_map(&PERSON_SCHEMA, &mut |_k, _d| Ok(()))
1287 .expect_err("empty body must be rejected by read_map");
1288 }
1289
1290 fn settings_with_max_depth(n: u32) -> Arc<XmlCodecSettings> {
1296 Arc::new(XmlCodecSettings::builder().max_depth(n).build())
1297 }
1298
1299 fn nested_struct_xml(depth: u32) -> Vec<u8> {
1301 let mut s = String::new();
1302 for _ in 0..depth {
1303 s.push_str("<r>");
1304 }
1305 s.push('v');
1306 for _ in 0..depth {
1307 s.push_str("</r>");
1308 }
1309 s.into_bytes()
1310 }
1311
1312 #[test]
1313 fn read_struct_rejects_overdeep_payloads() {
1314 static R_MEMBER_SELF: Schema<'static> =
1318 Schema::new_member(shape_id!("test", "R$r"), ShapeType::Structure, "r", 0);
1319 static R_SCHEMA: Schema<'static> = Schema::new_struct(
1320 shape_id!("test", "R"),
1321 ShapeType::Structure,
1322 &[&R_MEMBER_SELF],
1323 );
1324
1325 let max = 4;
1327 let xml = nested_struct_xml(max + 2);
1328 let mut deser = XmlDeserializer::new(&xml, settings_with_max_depth(max));
1329
1330 fn consume(_m: &Schema<'_>, d: &mut dyn ShapeDeserializer) -> Result<(), SerdeError> {
1332 d.read_struct(&R_SCHEMA, &mut consume)
1333 }
1334 let err = deser
1335 .read_struct(&R_SCHEMA, &mut consume)
1336 .expect_err("must reject payload exceeding max_depth");
1337 assert!(
1338 format!("{err}").contains("maximum nesting depth exceeded"),
1339 "expected depth-exceeded error, got: {err}"
1340 );
1341 }
1342
1343 #[test]
1344 fn read_struct_accepts_payloads_up_to_max_depth() {
1345 static R_MEMBER_SELF: Schema<'static> =
1350 Schema::new_member(shape_id!("test", "R$r"), ShapeType::Structure, "r", 0);
1351 static R_SCHEMA: Schema<'static> = Schema::new_struct(
1352 shape_id!("test", "R"),
1353 ShapeType::Structure,
1354 &[&R_MEMBER_SELF],
1355 );
1356
1357 let max = 4;
1361 let xml = nested_struct_xml(max);
1362 let mut deser = XmlDeserializer::new(&xml, settings_with_max_depth(max));
1363
1364 let mut depth_seen = 0u32;
1365 fn consume(
1366 _m: &Schema<'_>,
1367 d: &mut dyn ShapeDeserializer,
1368 depth_seen: &mut u32,
1369 ) -> Result<(), SerdeError> {
1370 *depth_seen += 1;
1371 d.read_struct(&R_SCHEMA, &mut |m, d2| consume(m, d2, depth_seen))
1372 }
1373
1374 deser
1375 .read_struct(&R_SCHEMA, &mut |m, d| consume(m, d, &mut depth_seen))
1376 .expect("payload at exactly max_depth must succeed");
1377 }
1378
1379 #[test]
1380 fn read_list_rejects_overdeep_payloads() {
1381 static L_MEMBER: Schema<'static> =
1383 Schema::new_member(shape_id!("test", "L$member"), ShapeType::List, "member", 0);
1384 static L_SCHEMA: Schema<'static> = Schema::new_list(shape_id!("test", "L"), &L_MEMBER);
1385
1386 let max = 3;
1387 let xml = b"<l><l><l><l><l/></l></l></l></l>";
1389 let mut deser = XmlDeserializer::new(xml, settings_with_max_depth(max));
1390
1391 fn consume(d: &mut dyn ShapeDeserializer) -> Result<(), SerdeError> {
1392 d.read_list(&L_SCHEMA, &mut consume)
1393 }
1394 let err = deser
1395 .read_list(&L_SCHEMA, &mut consume)
1396 .expect_err("nested-list payload exceeding max_depth must error");
1397 assert!(
1398 format!("{err}").contains("maximum nesting depth exceeded"),
1399 "expected depth-exceeded error, got: {err}"
1400 );
1401 }
1402
1403 #[test]
1404 fn depth_resets_between_sibling_reads() {
1405 static R_MEMBER_SELF: Schema<'static> =
1408 Schema::new_member(shape_id!("test", "R$r"), ShapeType::Structure, "r", 0);
1409 static R_SCHEMA: Schema<'static> = Schema::new_struct(
1410 shape_id!("test", "R"),
1411 ShapeType::Structure,
1412 &[&R_MEMBER_SELF],
1413 );
1414
1415 let max = 4;
1416 let xml = nested_struct_xml(2);
1417 let mut deser = XmlDeserializer::new(&xml, settings_with_max_depth(max));
1418
1419 deser
1421 .read_struct(&R_SCHEMA, &mut |_m, d| {
1422 d.read_struct(&R_SCHEMA, &mut |_, _| Ok(()))
1423 })
1424 .expect("first read at depth=2 must succeed");
1425
1426 deser
1429 .read_struct(&R_SCHEMA, &mut |_m, d| {
1430 d.read_struct(&R_SCHEMA, &mut |_, _| Ok(()))
1431 })
1432 .expect("sibling read on the same deserializer must succeed");
1433 }
1434
1435 #[test]
1436 fn depth_resets_after_consumer_error() {
1437 static L_MEMBER: Schema<'static> = Schema::new_member(
1450 shape_id!("test", "L$member"),
1451 ShapeType::String,
1452 "member",
1453 0,
1454 );
1455 static L_SCHEMA: Schema<'static> = Schema::new_list(shape_id!("test", "L"), &L_MEMBER);
1456
1457 let max = 2;
1463 let xml = b"<l><member>a</member><member>b</member></l>";
1464 let mut deser = XmlDeserializer::new(xml, settings_with_max_depth(max));
1465
1466 let mut force_err = |_: &mut dyn ShapeDeserializer| -> Result<(), SerdeError> {
1468 Err(SerdeError::custom("forced"))
1469 };
1470 deser
1471 .read_list(&L_SCHEMA, &mut force_err)
1472 .expect_err("forced consumer error must propagate");
1473
1474 for i in 0..(max as usize + 5) {
1478 deser
1479 .read_list(&L_SCHEMA, &mut force_err)
1480 .expect_err(&format!("forced consumer error must propagate (iter {i})"));
1481 }
1482
1483 let mut count = 0usize;
1486 deser
1487 .read_list(&L_SCHEMA, &mut |d| {
1488 count += 1;
1489 d.read_string(&L_MEMBER).map(|_| ())
1490 })
1491 .expect("subsequent successful read must not be poisoned by prior errors");
1492 assert_eq!(count, 2);
1493 }
1494
1495 #[test]
1496 fn read_struct_unwrapped_output_with_prolog() {
1497 static MEMBER: Schema<'static> = Schema::new_member(
1511 shape_id!("test", "U$location"),
1512 ShapeType::String,
1513 "LocationConstraint",
1514 0,
1515 );
1516 static SCHEMA: Schema<'static> =
1517 Schema::new_struct(shape_id!("test", "U"), ShapeType::Structure, &[&MEMBER])
1518 .with_xml_unwrapped_output();
1519
1520 let xml = b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<LocationConstraint>us-west-2</LocationConstraint>";
1521 let mut deser = XmlDeserializer::new(xml, Arc::new(XmlCodecSettings::default()));
1522
1523 let mut got = String::new();
1524 deser
1525 .read_struct(&SCHEMA, &mut |member, d| {
1526 got = d.read_string(member)?;
1527 Ok(())
1528 })
1529 .expect("unwrapped output must deserialize through prolog");
1530 assert_eq!(got, "us-west-2");
1531 }
1532
1533 #[test]
1545 fn find_element_slice_handles_multibyte_utf8() {
1546 static KEY_MEMBER: Schema<'static> =
1551 Schema::new_member(shape_id!("test", "Wrapper$k"), ShapeType::Map, "entries", 0);
1552 static MAP_SCHEMA: Schema<'static> = Schema::new_map(
1553 shape_id!("test", "MyMap"),
1554 &aws_smithy_schema::prelude::STRING,
1555 &aws_smithy_schema::prelude::STRING,
1556 );
1557 static WRAPPER: Schema<'static> = Schema::new_struct(
1558 shape_id!("test", "Wrapper"),
1559 ShapeType::Structure,
1560 &[&KEY_MEMBER],
1561 );
1562 let _ = MAP_SCHEMA; let xml =
1565 "<Wrapper><entries><entry><key>Б</key><value></value></entry></entries></Wrapper>";
1566
1567 let settings = Arc::new(XmlCodecSettings::default());
1568 let mut deser = XmlDeserializer::new(xml.as_bytes(), settings);
1569
1570 let mut entries = Vec::new();
1571 deser
1572 .read_struct(&WRAPPER, &mut |member, d| {
1573 d.read_map(member, &mut |k, d| {
1574 entries.push((k, d.read_string(&aws_smithy_schema::prelude::STRING)?));
1575 Ok(())
1576 })
1577 })
1578 .expect("must not panic on multi-byte UTF-8 inside map elements");
1579
1580 assert_eq!(entries, vec![("Б".to_owned(), String::new())]);
1581 }
1582
1583 #[test]
1584 fn find_depth2_by_predicate_wrapped() {
1585 let xml = b"<Resp><FooResult><A>1</A></FooResult><Metadata/></Resp>";
1586 let got = find_depth2_element_slice_by(xml, |n| n.ends_with("Result"));
1587 assert_eq!(got, Some(&b"<FooResult><A>1</A></FooResult>"[..]));
1588 }
1589
1590 #[test]
1591 fn find_depth2_by_predicate_self_closing() {
1592 let xml = b"<Resp><FooResult/><Metadata><Id>r</Id></Metadata></Resp>";
1595 let got = find_depth2_element_slice_by(xml, |n| n.ends_with("Result"));
1596 assert_eq!(got, Some(&b"<FooResult/>"[..]));
1597 }
1598
1599 #[test]
1600 fn find_depth2_by_predicate_root_match() {
1601 let xml = b"<Error><Code>Boom</Code></Error>";
1603 let got = find_depth2_element_slice_by(xml, |n| n == "Error");
1604 assert_eq!(got, Some(&xml[..]));
1605 }
1606
1607 #[test]
1608 fn find_depth2_by_predicate_no_match() {
1609 let xml = b"<Resp><Metadata/></Resp>";
1610 assert_eq!(find_depth2_element_slice_by(xml, |n| n == "Error"), None);
1611 }
1612
1613 #[test]
1614 fn find_depth2_by_predicate_invalid_xml() {
1615 assert_eq!(find_depth2_element_slice_by(b"not xml", |_| true), None);
1616 }
1617
1618 #[test]
1626 fn read_string_list_helper() {
1627 let xml = b"<items><member>a</member><member>b</member><member></member></items>";
1628 let settings = Arc::new(XmlCodecSettings::default());
1629 let mut deser = XmlDeserializer::new(xml, settings);
1630
1631 static LIST_MEMBER: Schema<'static> = Schema::new_member(
1632 shape_id!("test", "L$member"),
1633 ShapeType::String,
1634 "member",
1635 0,
1636 );
1637 static LIST_SCHEMA: Schema<'static> =
1638 Schema::new_list(shape_id!("test", "L"), &LIST_MEMBER);
1639
1640 let out = deser.read_string_list(&LIST_SCHEMA).unwrap();
1641 assert_eq!(out, vec!["a".to_owned(), "b".to_owned(), String::new()]);
1642 }
1643
1644 #[test]
1645 fn read_string_list_helper_renamed_member_name() {
1646 let xml = b"<items><Item>x</Item><Item>y</Item></items>";
1651 let settings = Arc::new(XmlCodecSettings::default());
1652 let mut deser = XmlDeserializer::new(xml, settings);
1653
1654 static LIST_MEMBER: Schema<'static> = Schema::new_member(
1655 shape_id!("test", "L$member"),
1656 ShapeType::String,
1657 "member",
1658 0,
1659 );
1660 static LIST_SCHEMA: Schema<'static> =
1661 Schema::new_list(shape_id!("test", "L"), &LIST_MEMBER);
1662
1663 let out = deser.read_string_list(&LIST_SCHEMA).unwrap();
1664 assert_eq!(out, vec!["x".to_owned(), "y".to_owned()]);
1665 }
1666
1667 #[test]
1668 fn read_blob_list_helper() {
1669 let xml = b"<blobs><member>aGVsbG8=</member><member>d29ybGQ=</member></blobs>";
1671 let settings = Arc::new(XmlCodecSettings::default());
1672 let mut deser = XmlDeserializer::new(xml, settings);
1673
1674 static LIST_MEMBER: Schema<'static> =
1675 Schema::new_member(shape_id!("test", "B$member"), ShapeType::Blob, "member", 0);
1676 static LIST_SCHEMA: Schema<'static> =
1677 Schema::new_list(shape_id!("test", "B"), &LIST_MEMBER);
1678
1679 let out = deser.read_blob_list(&LIST_SCHEMA).unwrap();
1680 assert_eq!(out.len(), 2);
1681 assert_eq!(out[0].as_ref(), b"hello");
1682 assert_eq!(out[1].as_ref(), b"world");
1683 }
1684
1685 #[test]
1686 fn read_blob_list_helper_rejects_invalid_base64() {
1687 let xml = b"<blobs><member>!!!not-base64!!!</member></blobs>";
1688 let settings = Arc::new(XmlCodecSettings::default());
1689 let mut deser = XmlDeserializer::new(xml, settings);
1690
1691 static LIST_MEMBER: Schema<'static> =
1692 Schema::new_member(shape_id!("test", "B$member"), ShapeType::Blob, "member", 0);
1693 static LIST_SCHEMA: Schema<'static> =
1694 Schema::new_list(shape_id!("test", "B"), &LIST_MEMBER);
1695
1696 let err = deser.read_blob_list(&LIST_SCHEMA).unwrap_err();
1697 assert!(format!("{err}").contains("base64"));
1698 }
1699
1700 #[test]
1701 fn read_integer_list_helper() {
1702 let xml = b"<nums><member>1</member><member>-42</member><member>0</member></nums>";
1703 let settings = Arc::new(XmlCodecSettings::default());
1704 let mut deser = XmlDeserializer::new(xml, settings);
1705
1706 static LIST_MEMBER: Schema<'static> = Schema::new_member(
1707 shape_id!("test", "I$member"),
1708 ShapeType::Integer,
1709 "member",
1710 0,
1711 );
1712 static LIST_SCHEMA: Schema<'static> =
1713 Schema::new_list(shape_id!("test", "I"), &LIST_MEMBER);
1714
1715 let out = deser.read_integer_list(&LIST_SCHEMA).unwrap();
1716 assert_eq!(out, vec![1i32, -42, 0]);
1717 }
1718
1719 #[test]
1720 fn read_long_list_helper() {
1721 let xml = b"<nums><member>9223372036854775807</member><member>-1</member></nums>";
1722 let settings = Arc::new(XmlCodecSettings::default());
1723 let mut deser = XmlDeserializer::new(xml, settings);
1724
1725 static LIST_MEMBER: Schema<'static> =
1726 Schema::new_member(shape_id!("test", "Lo$member"), ShapeType::Long, "member", 0);
1727 static LIST_SCHEMA: Schema<'static> =
1728 Schema::new_list(shape_id!("test", "Lo"), &LIST_MEMBER);
1729
1730 let out = deser.read_long_list(&LIST_SCHEMA).unwrap();
1731 assert_eq!(out, vec![i64::MAX, -1]);
1732 }
1733
1734 #[test]
1735 fn read_string_string_map_helper() {
1736 let xml = b"<m><entry><key>a</key><value>1</value></entry>\
1737 <entry><key>b</key><value>2</value></entry></m>";
1738 let settings = Arc::new(XmlCodecSettings::default());
1739 let mut deser = XmlDeserializer::new(xml, settings);
1740
1741 static MAP_KEY: Schema<'static> =
1742 Schema::new_member(shape_id!("test", "M$key"), ShapeType::String, "key", 0);
1743 static MAP_VALUE: Schema<'static> =
1744 Schema::new_member(shape_id!("test", "M$value"), ShapeType::String, "value", 0);
1745 static MAP_SCHEMA: Schema<'static> =
1746 Schema::new_map(shape_id!("test", "M"), &MAP_KEY, &MAP_VALUE);
1747
1748 let out = deser.read_string_string_map(&MAP_SCHEMA).unwrap();
1749 assert_eq!(out.len(), 2);
1750 assert_eq!(out.get("a").map(String::as_str), Some("1"));
1751 assert_eq!(out.get("b").map(String::as_str), Some("2"));
1752 }
1753
1754 #[test]
1755 fn read_string_string_map_helper_with_renamed_key_value() {
1756 let xml = b"<m><entry><K>a</K><V>1</V></entry></m>";
1759 let settings = Arc::new(XmlCodecSettings::default());
1760 let mut deser = XmlDeserializer::new(xml, settings);
1761
1762 static MAP_KEY: Schema<'static> =
1763 Schema::new_member(shape_id!("test", "M2$key"), ShapeType::String, "key", 0)
1764 .with_xml_name("K");
1765 static MAP_VALUE: Schema<'static> =
1766 Schema::new_member(shape_id!("test", "M2$value"), ShapeType::String, "value", 0)
1767 .with_xml_name("V");
1768 static MAP_SCHEMA: Schema<'static> =
1769 Schema::new_map(shape_id!("test", "M2"), &MAP_KEY, &MAP_VALUE);
1770
1771 let out = deser.read_string_string_map(&MAP_SCHEMA).unwrap();
1772 assert_eq!(out.get("a").map(String::as_str), Some("1"));
1773 }
1774
1775 #[test]
1785 fn nested_struct_three_levels_deep() {
1786 static LEAF: Schema<'static> =
1787 Schema::new_member(shape_id!("t", "Inner"), ShapeType::String, "Leaf", 0);
1788 static INNER_SCHEMA: Schema<'static> =
1789 Schema::new_struct(shape_id!("t", "Inner"), ShapeType::Structure, &[&LEAF]);
1790 static INNER_MEMBER: Schema<'static> =
1791 Schema::new_member(shape_id!("t", "Middle"), ShapeType::Structure, "Inner", 0);
1792 static MIDDLE_SCHEMA: Schema<'static> = Schema::new_struct(
1793 shape_id!("t", "Middle"),
1794 ShapeType::Structure,
1795 &[&INNER_MEMBER],
1796 );
1797 static MIDDLE_MEMBER: Schema<'static> =
1798 Schema::new_member(shape_id!("t", "Outer"), ShapeType::Structure, "Middle", 0);
1799 static OUTER_SCHEMA: Schema<'static> = Schema::new_struct(
1800 shape_id!("t", "Outer"),
1801 ShapeType::Structure,
1802 &[&MIDDLE_MEMBER],
1803 );
1804 static OUTER_MEMBER: Schema<'static> =
1805 Schema::new_member(shape_id!("t", "Root"), ShapeType::Structure, "Outer", 0);
1806 static ROOT: Schema<'static> = Schema::new_struct(
1807 shape_id!("t", "Root"),
1808 ShapeType::Structure,
1809 &[&OUTER_MEMBER],
1810 );
1811
1812 let xml = b"<Root><Outer><Middle><Inner><Leaf>value</Leaf></Inner></Middle></Outer></Root>";
1813 let settings = Arc::new(XmlCodecSettings::default());
1814 let mut deser = XmlDeserializer::new(xml, settings);
1815 let mut leaf = String::new();
1816 deser
1817 .read_struct(&ROOT, &mut |outer_m, d_outer| {
1818 assert_eq!(outer_m.member_name(), Some("Outer"));
1819 d_outer.read_struct(&OUTER_SCHEMA, &mut |middle_m, d_middle| {
1820 assert_eq!(middle_m.member_name(), Some("Middle"));
1821 d_middle.read_struct(&MIDDLE_SCHEMA, &mut |inner_m, d_inner| {
1822 assert_eq!(inner_m.member_name(), Some("Inner"));
1823 d_inner.read_struct(&INNER_SCHEMA, &mut |leaf_m, d_leaf| {
1824 if leaf_m.member_name() == Some("Leaf") {
1825 leaf = d_leaf.read_string(leaf_m)?;
1826 }
1827 Ok(())
1828 })
1829 })
1830 })
1831 })
1832 .expect("3-level nested struct should round-trip");
1833 assert_eq!(leaf, "value");
1834 }
1835
1836 #[test]
1837 fn struct_member_with_escaped_text_round_trips() {
1838 static VALUE: Schema<'static> =
1839 Schema::new_member(shape_id!("t", "Body"), ShapeType::String, "value", 0);
1840 static BODY_SCHEMA: Schema<'static> =
1841 Schema::new_struct(shape_id!("t", "Body"), ShapeType::Structure, &[&VALUE]);
1842 static PAYLOAD_MEMBER: Schema<'static> = Schema::new_member(
1843 shape_id!("t", "Envelope"),
1844 ShapeType::Structure,
1845 "payload",
1846 0,
1847 );
1848 static ENVELOPE_SCHEMA: Schema<'static> = Schema::new_struct(
1849 shape_id!("t", "Envelope"),
1850 ShapeType::Structure,
1851 &[&PAYLOAD_MEMBER],
1852 );
1853
1854 let xml = b"<Envelope><payload><value>foo&bar</value></payload></Envelope>";
1859 let settings = Arc::new(XmlCodecSettings::default());
1860 let mut deser = XmlDeserializer::new(xml, settings);
1861 let mut value_str = String::new();
1862 deser
1863 .read_struct(&ENVELOPE_SCHEMA, &mut |payload_m, d_payload| {
1864 let _ = payload_m;
1865 d_payload.read_struct(&BODY_SCHEMA, &mut |inner_m, d_inner| {
1866 if inner_m.member_name() == Some("value") {
1867 value_str = d_inner.read_string(inner_m)?;
1868 }
1869 Ok(())
1870 })
1871 })
1872 .expect("escaped text inside a nested struct should round-trip");
1873 assert_eq!(value_str, "foo&bar");
1874 }
1875
1876 #[test]
1877 fn read_map_preserves_empty_string_key() {
1878 static KEY: Schema<'static> =
1881 Schema::new_member(shape_id!("t", "M$key"), ShapeType::String, "key", 0);
1882 static VALUE: Schema<'static> =
1883 Schema::new_member(shape_id!("t", "M$value"), ShapeType::String, "value", 1);
1884 static MAP: Schema<'static> = Schema::new_map(shape_id!("t", "M"), &KEY, &VALUE);
1885
1886 let xml = b"<Root><entry><key></key><value>v1</value></entry></Root>";
1887 let settings = Arc::new(XmlCodecSettings::default());
1888 let mut deser = XmlDeserializer::new(xml, settings);
1889
1890 let mut got: std::collections::HashMap<String, String> = Default::default();
1891 deser
1892 .read_map(&MAP, &mut |k, d| {
1893 let v = d.read_string(&VALUE)?;
1894 got.insert(k, v);
1895 Ok(())
1896 })
1897 .expect("empty-key entry should be preserved");
1898 assert_eq!(got.get("").map(String::as_str), Some("v1"));
1899 }
1900
1901 #[test]
1907 fn read_map_value_is_inner_map_with_renamed_inner_key_value() {
1908 let xml = b"<outerMap><entry><key>ok</key><value><entry><InnerKey>ik</InnerKey><InnerVal>iv</InnerVal></entry></value></entry></outerMap>";
1909 let settings = Arc::new(XmlCodecSettings::default());
1910 let mut deser = XmlDeserializer::new(xml, settings);
1911
1912 static INNER_KEY: Schema<'static> = Schema::new_member(
1913 shape_id!("test", "InnerMap$key"),
1914 ShapeType::String,
1915 "key",
1916 0,
1917 )
1918 .with_xml_name("InnerKey");
1919 static INNER_VAL: Schema<'static> = Schema::new_member(
1920 shape_id!("test", "InnerMap$value"),
1921 ShapeType::String,
1922 "value",
1923 1,
1924 )
1925 .with_xml_name("InnerVal");
1926
1927 static OUTER_VALUE: Schema<'static> = Schema::new_member(
1931 shape_id!("test", "OuterMap$value"),
1932 ShapeType::Map,
1933 "value",
1934 1,
1935 )
1936 .with_map_members(&INNER_KEY, &INNER_VAL);
1937
1938 static OUTER_KEY: Schema<'static> = Schema::new_member(
1939 shape_id!("test", "OuterMap$key"),
1940 ShapeType::String,
1941 "key",
1942 0,
1943 );
1944 static OUTER_MAP_SCHEMA: Schema<'static> =
1945 Schema::new_map(shape_id!("test", "OuterMap"), &OUTER_KEY, &OUTER_VALUE);
1946
1947 let mut got: Vec<(String, Vec<(String, String)>)> = Vec::new();
1948 deser
1949 .read_map(&OUTER_MAP_SCHEMA, &mut |outer_key, d| {
1950 let mut inner: Vec<(String, String)> = Vec::new();
1951 d.read_map(&OUTER_VALUE, &mut |inner_key, d2| {
1952 let inner_val = d2.read_string(&INNER_VAL)?;
1953 inner.push((inner_key, inner_val));
1954 Ok(())
1955 })?;
1956 got.push((outer_key, inner));
1957 Ok(())
1958 })
1959 .expect("nested map deserialization should succeed");
1960
1961 assert_eq!(
1962 got,
1963 vec![("ok".to_owned(), vec![("ik".to_owned(), "iv".to_owned())])]
1964 );
1965 }
1966
1967 #[test]
1972 fn read_list_member_is_inner_map_with_renamed_inner_key_value() {
1973 let xml = b"<items><member><entry><Attr>k1</Attr><Set>v1</Set></entry></member></items>";
1974 let settings = Arc::new(XmlCodecSettings::default());
1975 let mut deser = XmlDeserializer::new(xml, settings);
1976
1977 static INNER_KEY: Schema<'static> = Schema::new_member(
1978 shape_id!("test", "InnerMap$key"),
1979 ShapeType::String,
1980 "key",
1981 0,
1982 )
1983 .with_xml_name("Attr");
1984 static INNER_VAL: Schema<'static> = Schema::new_member(
1985 shape_id!("test", "InnerMap$value"),
1986 ShapeType::String,
1987 "value",
1988 1,
1989 )
1990 .with_xml_name("Set");
1991
1992 static LIST_ITEM: Schema<'static> = Schema::new_member(
1995 shape_id!("test", "OuterList$member"),
1996 ShapeType::Map,
1997 "member",
1998 0,
1999 )
2000 .with_map_members(&INNER_KEY, &INNER_VAL);
2001
2002 static OUTER_LIST_SCHEMA: Schema<'static> =
2003 Schema::new_list(shape_id!("test", "OuterList"), &LIST_ITEM);
2004
2005 let mut got: Vec<Vec<(String, String)>> = Vec::new();
2006 deser
2007 .read_list(&OUTER_LIST_SCHEMA, &mut |d| {
2008 let mut entries: Vec<(String, String)> = Vec::new();
2009 d.read_map(&LIST_ITEM, &mut |inner_key, d2| {
2010 let inner_val = d2.read_string(&INNER_VAL)?;
2011 entries.push((inner_key, inner_val));
2012 Ok(())
2013 })?;
2014 got.push(entries);
2015 Ok(())
2016 })
2017 .expect("list-of-map deserialization should succeed");
2018
2019 assert_eq!(got, vec![vec![("k1".to_owned(), "v1".to_owned())]]);
2020 }
2021}