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, Document as SmithyDocument};
14use std::borrow::Cow;
15use std::sync::Arc;
16
17pub(crate) const MAX_DESERIALIZE_DEPTH: u32 = 128;
21
22pub struct XmlDeserializer<'a> {
39 input: &'a [u8],
43 text: Option<Cow<'a, str>>,
46 settings: Arc<XmlCodecSettings>,
47 schema_override: Option<&'static Schema>,
56 depth: u32,
62}
63
64impl<'a> XmlDeserializer<'a> {
65 pub(crate) fn new(input: &'a [u8], settings: Arc<XmlCodecSettings>) -> Self {
67 Self {
68 input,
69 text: None,
70 settings,
71 schema_override: None,
72 depth: 0,
73 }
74 }
75
76 #[cfg(test)]
81 fn from_text(text: Cow<'a, str>, settings: Arc<XmlCodecSettings>) -> Self {
82 Self {
83 input: b"",
84 text: Some(text),
85 settings,
86 schema_override: None,
87 depth: 0,
88 }
89 }
90
91 fn enter_aggregate(&mut self) -> Result<(), SerdeError> {
101 if self.depth >= self.settings.max_depth() {
102 return Err(SerdeError::custom("maximum nesting depth exceeded"));
103 }
104 self.depth += 1;
105 Ok(())
106 }
107
108 fn leave_aggregate(&mut self) {
111 debug_assert!(self.depth > 0, "leave_aggregate without enter_aggregate");
112 self.depth = self.depth.saturating_sub(1);
113 }
114
115 fn document(&self) -> Result<Document<'a>, SerdeError> {
119 if self.text.is_some() {
120 return Err(SerdeError::custom("expected XML element, found text"));
121 }
122 Ok(Document::try_from(self.input).unwrap_or_else(|_| Document::new("")))
123 }
124
125 fn take_text(&mut self) -> Result<Cow<'a, str>, SerdeError> {
129 if let Some(t) = self.text.take() {
130 return Ok(t);
131 }
132 let mut doc = Document::try_from(self.input).unwrap_or_else(|_| Document::new(""));
133 let mut root = doc
134 .root_element()
135 .map_err(|e| SerdeError::custom(e.to_string()))?;
136 decode::try_data(&mut root).map_err(|e| SerdeError::custom(e.to_string()))
137 }
138
139 fn dispatch_subslice<R>(
144 &mut self,
145 sub: &'a [u8],
146 schema_override: Option<&'static Schema>,
147 f: impl FnOnce(&mut Self) -> R,
148 ) -> R {
149 let saved_input = std::mem::replace(&mut self.input, sub);
150 let saved_text = self.text.take();
151 let saved_override = std::mem::replace(&mut self.schema_override, schema_override);
152 let r = f(self);
153 self.input = saved_input;
154 self.text = saved_text;
155 self.schema_override = saved_override;
156 r
157 }
158
159 fn dispatch_text<R>(&mut self, text: Cow<'a, str>, f: impl FnOnce(&mut Self) -> R) -> R {
162 let saved_input = std::mem::replace(&mut self.input, b"");
163 let saved_text = self.text.replace(text);
164 let saved_override = self.schema_override.take();
165 let r = f(self);
166 self.input = saved_input;
167 self.text = saved_text;
168 self.schema_override = saved_override;
169 r
170 }
171
172 fn resolve_member<'s>(schema: &'s Schema, element_name: &str) -> Option<&'s Schema> {
175 schema.members().iter().copied().find(|m| {
176 if let Some(xml_name) = m.xml_name() {
177 xml_name.value() == element_name
178 } else {
179 m.member_name() == Some(element_name)
180 }
181 })
182 }
183
184 pub(crate) fn find_element_slice(input: &'a [u8], el_local: &str) -> &'a [u8] {
198 debug_assert!(
206 {
207 let lo = input.as_ptr() as usize;
208 let hi = lo + input.len();
209 let p = el_local.as_ptr() as usize;
210 p >= lo && p + el_local.len() <= hi
211 },
212 "find_element_slice: el_local must point into input"
213 );
214 let name_ptr = el_local.as_ptr() as usize;
215 let input_start = input.as_ptr() as usize;
216 let name_offset = name_ptr.saturating_sub(input_start).min(input.len());
217
218 let el_start = input[..name_offset]
221 .iter()
222 .rposition(|&b| b == b'<')
223 .unwrap_or(0);
224
225 let tag_name = el_local.as_bytes();
229 let remaining = &input[el_start..];
230 let mut depth = 0i32;
231 let mut pos = 0;
232 while pos < remaining.len() {
233 if remaining[pos..].starts_with(b"</") {
234 let after_slash = pos + 2;
236 if remaining[after_slash..].starts_with(tag_name) {
237 let after_name = after_slash + tag_name.len();
238 if remaining.get(after_name) == Some(&b'>') {
239 depth -= 1;
240 if depth == 0 {
241 let end = el_start + after_name + 1;
242 return &input[el_start..end];
243 }
244 }
245 }
246 pos = after_slash;
247 } else if remaining[pos] == b'<'
248 && remaining.get(pos + 1) != Some(&b'/')
249 && remaining.get(pos + 1) != Some(&b'?')
250 {
251 if let Some(gt) = remaining[pos..].iter().position(|&b| b == b'>') {
253 let tag_content = &remaining[pos + 1..pos + gt];
254 let is_self_closing = tag_content.last() == Some(&b'/');
255 let opens_our_tag = tag_content.starts_with(tag_name)
256 && tag_content
257 .get(tag_name.len())
258 .is_none_or(|&b| b == b' ' || b == b'>' || b == b'/');
259 if opens_our_tag && is_self_closing && depth == 0 {
260 let end = el_start + pos + gt + 1;
264 return &input[el_start..end];
265 }
266 if opens_our_tag && !is_self_closing {
267 depth += 1;
268 }
269 pos += gt + 1;
270 } else {
271 pos += 1;
272 }
273 } else {
274 pos += 1;
280 }
281 }
282 &input[el_start..]
284 }
285
286 fn resolve_timestamp_format(&self, schema: &Schema) -> TimestampFormat {
287 schema
288 .timestamp_format()
289 .map(|t| match t.format() {
290 aws_smithy_schema::traits::TimestampFormat::EpochSeconds => {
291 TimestampFormat::EpochSeconds
292 }
293 aws_smithy_schema::traits::TimestampFormat::DateTime => {
298 TimestampFormat::DateTimeWithOffset
299 }
300 aws_smithy_schema::traits::TimestampFormat::HttpDate => TimestampFormat::HttpDate,
301 })
302 .unwrap_or_else(|| match self.settings.default_timestamp_format() {
303 TimestampFormat::DateTime => TimestampFormat::DateTimeWithOffset,
304 other => other,
305 })
306 }
307}
308
309pub fn find_depth2_element_slice_by(
323 body: &[u8],
324 predicate: impl Fn(&str) -> bool,
325) -> Option<&[u8]> {
326 let mut doc = Document::try_from(body).ok()?;
327 let mut root = doc.root_element().ok()?;
328 if predicate(root.start_el().local()) {
331 return Some(body);
332 }
333 while let Some(tag) = root.next_tag() {
335 let local = tag.start_el().local();
336 if predicate(local) {
337 return Some(XmlDeserializer::find_element_slice(body, local));
340 }
341 }
342 None
343}
344
345impl ShapeDeserializer for XmlDeserializer<'_> {
346 fn read_struct(
347 &mut self,
348 schema: &Schema,
349 consumer: &mut dyn FnMut(&Schema, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
350 ) -> Result<(), SerdeError> {
351 self.enter_aggregate()?;
352 let result = (|| -> Result<(), SerdeError> {
358 let input = self.input;
364 let mut doc = self.document()?;
365 let mut root = doc
366 .root_element()
367 .map_err(|e| SerdeError::custom(e.to_string()))?;
368
369 if schema.xml_unwrapped_output() {
378 let el_local = root.start_el().local();
390 let sub = Self::find_element_slice(input, el_local);
391 let local = el_local.to_owned();
392 drop(root);
398 let _ = doc;
399 if let Some(member) = Self::resolve_member(schema, &local) {
400 self.dispatch_subslice(sub, None, |this| consumer(member, this))?;
401 }
402 return Ok(());
403 }
404
405 for member in schema.members() {
407 if member.xml_attribute() {
408 let attr_name = member
409 .xml_name()
410 .map(|t| t.value())
411 .or(member.member_name())
412 .unwrap_or("");
413 if let Some(value) = root.start_el().attr(attr_name) {
414 let text = Cow::Owned(value.to_owned());
415 self.dispatch_text(text, |this| consumer(member, this))?;
416 }
417 }
418 }
419
420 let mut flattened_groups: std::collections::HashMap<usize, (&Schema, Vec<u8>)> =
424 std::collections::HashMap::new();
425
426 while let Some(mut child_scope) = root.next_tag() {
428 let local = child_scope.start_el().local().to_owned();
429 let Some(member) = Self::resolve_member(schema, &local) else {
430 continue;
431 };
432 let is_aggregate = member.shape_type().is_aggregate();
440 if is_aggregate && !member.xml_flattened() {
441 let el_local = child_scope.start_el().local();
442 let sub = Self::find_element_slice(input, el_local);
443 drop(child_scope);
444 self.dispatch_subslice(sub, None, |this| consumer(member, this))?;
445 } else if is_aggregate {
446 let el_local = child_scope.start_el().local();
449 let sub = Self::find_element_slice(input, el_local);
450 drop(child_scope);
451 let idx = member.member_index().unwrap_or(usize::MAX);
452 let entry = flattened_groups
453 .entry(idx)
454 .or_insert_with(|| (member, Vec::new()));
455 entry.1.extend_from_slice(sub);
456 } else {
457 let text = decode::try_data(&mut child_scope)
458 .map_err(|e| SerdeError::custom(e.to_string()))?;
459 drop(child_scope);
460 self.dispatch_text(text, |this| consumer(member, this))?;
461 }
462 }
463
464 for (_idx, (member, bytes)) in flattened_groups {
472 let mut wrapped = Vec::with_capacity(bytes.len() + 16);
473 wrapped.extend_from_slice(b"<__flat>");
474 wrapped.extend_from_slice(&bytes);
475 wrapped.extend_from_slice(b"</__flat>");
476 let mut child_deser = XmlDeserializer::new(&wrapped, self.settings.clone());
477 consumer(member, &mut child_deser)?;
478 }
479 Ok(())
480 })();
481 self.leave_aggregate();
482 result
483 }
484
485 fn read_list(
486 &mut self,
487 _schema: &Schema,
488 consumer: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
489 ) -> Result<(), SerdeError> {
490 self.enter_aggregate()?;
491 let result = (|| -> Result<(), SerdeError> {
494 let input = self.input;
495 let mut doc = self.document()?;
496 let mut root = doc
497 .root_element()
498 .map_err(|e| SerdeError::custom(e.to_string()))?;
499
500 while let Some(child_scope) = root.next_tag() {
509 let el_local = child_scope.start_el().local();
510 let sub = Self::find_element_slice(input, el_local);
511 drop(child_scope);
512 self.dispatch_subslice(sub, None, |this| consumer(this))?;
513 }
514 Ok(())
515 })();
516 self.leave_aggregate();
517 result
518 }
519
520 fn read_map(
521 &mut self,
522 schema: &Schema,
523 consumer: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
524 ) -> Result<(), SerdeError> {
525 self.enter_aggregate()?;
526 let result = (|| -> Result<(), SerdeError> {
529 let effective_schema: &Schema =
537 self.schema_override.map(|s| s as &Schema).unwrap_or(schema);
538 self.schema_override = None;
541 let schema = effective_schema;
542
543 let input = self.input;
544 let mut doc = self.document()?;
545 let mut root = doc
546 .root_element()
547 .map_err(|e| SerdeError::custom(e.to_string()))?;
548
549 let key_name = schema
551 .key()
552 .and_then(|k| k.xml_name().map(|t| t.value()))
553 .unwrap_or("key");
554 let value_name = schema
555 .member()
556 .and_then(|v| v.xml_name().map(|t| t.value()))
557 .unwrap_or("value");
558
559 let value_is_aggregate = schema
562 .member()
563 .map(|v| v.shape_type().is_aggregate())
564 .unwrap_or(false);
565 let value_schema_static = schema.member_static();
570
571 while let Some(mut entry_scope) = root.next_tag() {
573 let mut key: Option<String> = None;
574 let mut value_text: Option<Cow<'_, str>> = None;
577 let mut value_slice: Option<&'_ [u8]> = None;
578 while let Some(mut field_scope) = entry_scope.next_tag() {
579 let local = field_scope.start_el().local().to_owned();
580 if local == key_name {
581 let text = decode::try_data(&mut field_scope)
582 .map_err(|e| SerdeError::custom(e.to_string()))?;
583 key = Some(text.into_owned());
584 } else if local == value_name {
585 if value_is_aggregate {
586 let el_local = field_scope.start_el().local();
587 let sub = Self::find_element_slice(input, el_local);
588 drop(field_scope);
589 value_slice = Some(sub);
590 } else {
591 let text = decode::try_data(&mut field_scope)
592 .map_err(|e| SerdeError::custom(e.to_string()))?;
593 value_text = Some(text);
594 }
595 }
596 }
597 drop(entry_scope);
598 if let Some(k) = key {
599 if let Some(slice) = value_slice {
600 self.dispatch_subslice(slice, value_schema_static, |this| {
601 consumer(k, this)
602 })?;
603 } else if let Some(t) = value_text {
604 let t: Cow<'_, str> = t;
608 self.dispatch_text(t.into_owned().into(), |this| consumer(k, this))?;
609 }
610 }
611 }
612 Ok(())
613 })();
614 self.leave_aggregate();
615 result
616 }
617
618 fn read_boolean(&mut self, _schema: &Schema) -> Result<bool, SerdeError> {
619 let text = self.take_text()?;
620 match text.as_ref() {
621 "true" => Ok(true),
622 "false" => Ok(false),
623 other => Err(SerdeError::custom(format!("invalid boolean: {other}"))),
624 }
625 }
626
627 fn read_byte(&mut self, _schema: &Schema) -> Result<i8, SerdeError> {
628 let text = self.take_text()?;
629 text.parse().map_err(|e| SerdeError::custom(format!("{e}")))
630 }
631
632 fn read_short(&mut self, _schema: &Schema) -> Result<i16, SerdeError> {
633 let text = self.take_text()?;
634 text.parse().map_err(|e| SerdeError::custom(format!("{e}")))
635 }
636
637 fn read_integer(&mut self, _schema: &Schema) -> Result<i32, SerdeError> {
638 let text = self.take_text()?;
639 text.parse().map_err(|e| SerdeError::custom(format!("{e}")))
640 }
641
642 fn read_long(&mut self, _schema: &Schema) -> Result<i64, SerdeError> {
643 let text = self.take_text()?;
644 text.parse().map_err(|e| SerdeError::custom(format!("{e}")))
645 }
646
647 fn read_float(&mut self, _schema: &Schema) -> Result<f32, SerdeError> {
648 let text = self.take_text()?;
649 match text.as_ref() {
650 "NaN" => Ok(f32::NAN),
651 "Infinity" => Ok(f32::INFINITY),
652 "-Infinity" => Ok(f32::NEG_INFINITY),
653 _ => text.parse().map_err(|e| SerdeError::custom(format!("{e}"))),
654 }
655 }
656
657 fn read_double(&mut self, _schema: &Schema) -> Result<f64, SerdeError> {
658 let text = self.take_text()?;
659 match text.as_ref() {
660 "NaN" => Ok(f64::NAN),
661 "Infinity" => Ok(f64::INFINITY),
662 "-Infinity" => Ok(f64::NEG_INFINITY),
663 _ => text.parse().map_err(|e| SerdeError::custom(format!("{e}"))),
664 }
665 }
666
667 fn read_big_integer(&mut self, _schema: &Schema) -> Result<BigInteger, SerdeError> {
668 let text = self.take_text()?;
669 text.parse().map_err(|e| SerdeError::custom(format!("{e}")))
670 }
671
672 fn read_big_decimal(&mut self, _schema: &Schema) -> Result<BigDecimal, SerdeError> {
673 let text = self.take_text()?;
674 text.parse().map_err(|e| SerdeError::custom(format!("{e}")))
675 }
676
677 fn read_string(&mut self, _schema: &Schema) -> Result<String, SerdeError> {
678 let text = self.take_text()?;
679 Ok(text.into_owned())
680 }
681
682 fn read_blob(&mut self, _schema: &Schema) -> Result<Blob, SerdeError> {
683 let text = self.take_text()?;
684 let bytes = aws_smithy_types::base64::decode(text.as_ref())
685 .map_err(|e| SerdeError::custom(format!("{e}")))?;
686 Ok(Blob::new(bytes))
687 }
688
689 fn read_timestamp(&mut self, schema: &Schema) -> Result<DateTime, SerdeError> {
690 let text = self.take_text()?;
691 let format = self.resolve_timestamp_format(schema);
692 DateTime::from_str(text.as_ref(), format).map_err(|e| SerdeError::custom(format!("{e}")))
693 }
694
695 fn read_document(&mut self, _schema: &Schema) -> Result<SmithyDocument, SerdeError> {
696 Err(SerdeError::custom(
697 "document types are not supported by REST XML",
698 ))
699 }
700
701 fn read_string_list(&mut self, _schema: &Schema) -> Result<Vec<String>, SerdeError> {
728 self.enter_aggregate()?;
729 let result = (|| -> Result<Vec<String>, SerdeError> {
734 let mut doc = self.document()?;
735 let mut root = doc
736 .root_element()
737 .map_err(|e| SerdeError::custom(e.to_string()))?;
738 let mut out = Vec::new();
739 while let Some(mut child_scope) = root.next_tag() {
740 let text = decode::try_data(&mut child_scope)
741 .map_err(|e| SerdeError::custom(e.to_string()))?;
742 out.push(text.into_owned());
743 }
744 Ok(out)
745 })();
746 self.leave_aggregate();
747 result
748 }
749
750 fn read_blob_list(&mut self, _schema: &Schema) -> Result<Vec<Blob>, SerdeError> {
751 use aws_smithy_types::base64;
752 self.enter_aggregate()?;
753 let result = (|| -> Result<Vec<Blob>, SerdeError> {
754 let mut doc = self.document()?;
755 let mut root = doc
756 .root_element()
757 .map_err(|e| SerdeError::custom(e.to_string()))?;
758 let mut out = Vec::new();
759 while let Some(mut child_scope) = root.next_tag() {
760 let text = decode::try_data(&mut child_scope)
761 .map_err(|e| SerdeError::custom(e.to_string()))?;
762 let bytes = base64::decode(text.as_ref())
763 .map_err(|e| SerdeError::custom(format!("invalid base64: {e}")))?;
764 out.push(Blob::new(bytes));
765 }
766 Ok(out)
767 })();
768 self.leave_aggregate();
769 result
770 }
771
772 fn read_integer_list(&mut self, _schema: &Schema) -> Result<Vec<i32>, SerdeError> {
773 self.enter_aggregate()?;
774 let result = (|| -> Result<Vec<i32>, SerdeError> {
775 let mut doc = self.document()?;
776 let mut root = doc
777 .root_element()
778 .map_err(|e| SerdeError::custom(e.to_string()))?;
779 let mut out = Vec::new();
780 while let Some(mut child_scope) = root.next_tag() {
781 let text = decode::try_data(&mut child_scope)
782 .map_err(|e| SerdeError::custom(e.to_string()))?;
783 let v: i32 = text
784 .parse()
785 .map_err(|e| SerdeError::custom(format!("{e}")))?;
786 out.push(v);
787 }
788 Ok(out)
789 })();
790 self.leave_aggregate();
791 result
792 }
793
794 fn read_long_list(&mut self, _schema: &Schema) -> Result<Vec<i64>, SerdeError> {
795 self.enter_aggregate()?;
796 let result = (|| -> Result<Vec<i64>, SerdeError> {
797 let mut doc = self.document()?;
798 let mut root = doc
799 .root_element()
800 .map_err(|e| SerdeError::custom(e.to_string()))?;
801 let mut out = Vec::new();
802 while let Some(mut child_scope) = root.next_tag() {
803 let text = decode::try_data(&mut child_scope)
804 .map_err(|e| SerdeError::custom(e.to_string()))?;
805 let v: i64 = text
806 .parse()
807 .map_err(|e| SerdeError::custom(format!("{e}")))?;
808 out.push(v);
809 }
810 Ok(out)
811 })();
812 self.leave_aggregate();
813 result
814 }
815
816 fn read_string_string_map(
817 &mut self,
818 schema: &Schema,
819 ) -> Result<std::collections::HashMap<String, String>, SerdeError> {
820 let effective_schema: &Schema =
823 self.schema_override.map(|s| s as &Schema).unwrap_or(schema);
824 self.schema_override = None;
825 let schema = effective_schema;
826
827 let key_name = schema
828 .key()
829 .and_then(|k| k.xml_name().map(|t| t.value()))
830 .unwrap_or("key");
831 let value_name = schema
832 .member()
833 .and_then(|v| v.xml_name().map(|t| t.value()))
834 .unwrap_or("value");
835
836 self.enter_aggregate()?;
837 let result = (|| -> Result<std::collections::HashMap<String, String>, SerdeError> {
838 let mut doc = self.document()?;
839 let mut root = doc
840 .root_element()
841 .map_err(|e| SerdeError::custom(e.to_string()))?;
842 let mut out = std::collections::HashMap::new();
843 while let Some(mut entry_scope) = root.next_tag() {
844 let mut k: Option<String> = None;
845 let mut v: Option<String> = None;
846 while let Some(mut field_scope) = entry_scope.next_tag() {
847 let local = field_scope.start_el().local().to_owned();
848 if local == key_name {
849 let text = decode::try_data(&mut field_scope)
850 .map_err(|e| SerdeError::custom(e.to_string()))?;
851 k = Some(text.into_owned());
852 } else if local == value_name {
853 let text = decode::try_data(&mut field_scope)
854 .map_err(|e| SerdeError::custom(e.to_string()))?;
855 v = Some(text.into_owned());
856 }
857 }
858 if let (Some(k), Some(v)) = (k, v) {
859 out.insert(k, v);
860 }
861 }
862 Ok(out)
863 })();
864 self.leave_aggregate();
865 result
866 }
867
868 fn is_null(&self) -> bool {
869 false
872 }
873
874 fn container_size(&self) -> Option<usize> {
875 None
876 }
877}
878
879#[cfg(test)]
880mod tests {
881 use super::*;
882 use aws_smithy_schema::{shape_id, Schema, ShapeType};
883
884 static STRING_MEMBER: Schema =
885 Schema::new_member(shape_id!("test", "S$v"), ShapeType::String, "v", 0);
886
887 #[test]
888 fn read_string_from_text_state() {
889 let settings = Arc::new(XmlCodecSettings::default());
890 let mut deser = XmlDeserializer::from_text(Cow::Borrowed("hello"), settings);
891 let result = deser.read_string(&STRING_MEMBER).unwrap();
892 assert_eq!(result, "hello");
893 }
894
895 #[test]
896 fn read_string_from_doc_text_content() {
897 static V_MEMBER: Schema =
901 Schema::new_member(shape_id!("test", "X$v"), ShapeType::String, "v", 0);
902 let xml = b"<root>world</root>";
903 let settings = Arc::new(XmlCodecSettings::default());
904 let mut deser = XmlDeserializer::new(xml, settings);
905 let result = deser.read_string(&V_MEMBER).unwrap();
906 assert_eq!(result, "world");
907 }
908
909 #[test]
910 fn is_null_always_false() {
911 let settings = Arc::new(XmlCodecSettings::default());
912 let deser = XmlDeserializer::new(b"<r/>", settings);
913 assert!(!deser.is_null());
914 }
915
916 static NAME_MEMBER: Schema = Schema::new_member(
919 shape_id!("test", "Person$name"),
920 ShapeType::String,
921 "name",
922 0,
923 );
924 static AGE_MEMBER: Schema =
925 Schema::new_member(shape_id!("test", "Person$age"), ShapeType::String, "age", 1);
926 static RENAMED_MEMBER: Schema = Schema::new_member(
927 shape_id!("test", "Person$nick"),
928 ShapeType::String,
929 "nick",
930 2,
931 )
932 .with_xml_name("Nickname");
933
934 static PERSON_SCHEMA: Schema = Schema::new_struct(
935 shape_id!("test", "Person"),
936 ShapeType::Structure,
937 &[&NAME_MEMBER, &AGE_MEMBER, &RENAMED_MEMBER],
938 );
939
940 #[test]
941 fn read_struct_dispatches_members() {
942 let xml = b"<Person><name>Alice</name><age>30</age></Person>";
943 let settings = Arc::new(XmlCodecSettings::default());
944 let mut deser = XmlDeserializer::new(xml, settings);
945
946 let mut name = String::new();
947 let mut age = String::new();
948 deser
949 .read_struct(&PERSON_SCHEMA, &mut |member, d| {
950 match member.member_name().unwrap() {
951 "name" => name = d.read_string(member)?,
952 "age" => age = d.read_string(member)?,
953 _ => {}
954 }
955 Ok(())
956 })
957 .unwrap();
958
959 assert_eq!(name, "Alice");
960 assert_eq!(age, "30");
961 }
962
963 #[test]
964 fn read_struct_skips_unknown_elements() {
965 let xml = b"<Person><unknown>x</unknown><name>Bob</name></Person>";
966 let settings = Arc::new(XmlCodecSettings::default());
967 let mut deser = XmlDeserializer::new(xml, settings);
968
969 let mut name = String::new();
970 deser
971 .read_struct(&PERSON_SCHEMA, &mut |member, d| {
972 if member.member_name() == Some("name") {
973 name = d.read_string(member)?;
974 }
975 Ok(())
976 })
977 .unwrap();
978
979 assert_eq!(name, "Bob");
980 }
981
982 #[test]
983 fn read_struct_resolves_xml_name() {
984 let xml = b"<Person><Nickname>Ally</Nickname></Person>";
985 let settings = Arc::new(XmlCodecSettings::default());
986 let mut deser = XmlDeserializer::new(xml, settings);
987
988 let mut nick = String::new();
989 deser
990 .read_struct(&PERSON_SCHEMA, &mut |member, d| {
991 if member.member_name() == Some("nick") {
992 nick = d.read_string(member)?;
993 }
994 Ok(())
995 })
996 .unwrap();
997
998 assert_eq!(nick, "Ally");
999 }
1000
1001 static ATTR_MEMBER: Schema =
1004 Schema::new_member(shape_id!("test", "X$id"), ShapeType::String, "id", 0)
1005 .with_xml_attribute();
1006 static ELEM_MEMBER: Schema =
1007 Schema::new_member(shape_id!("test", "X$name"), ShapeType::String, "name", 1);
1008
1009 static X_SCHEMA: Schema = Schema::new_struct(
1010 shape_id!("test", "X"),
1011 ShapeType::Structure,
1012 &[&ATTR_MEMBER, &ELEM_MEMBER],
1013 );
1014
1015 #[test]
1016 fn read_struct_dispatches_attributes() {
1017 let xml = b"<X id=\"42\"><name>hello</name></X>";
1018 let settings = Arc::new(XmlCodecSettings::default());
1019 let mut deser = XmlDeserializer::new(xml, settings);
1020
1021 let mut id = String::new();
1022 let mut name = String::new();
1023 deser
1024 .read_struct(&X_SCHEMA, &mut |member, d| {
1025 match member.member_name().unwrap() {
1026 "id" => id = d.read_string(member)?,
1027 "name" => name = d.read_string(member)?,
1028 _ => {}
1029 }
1030 Ok(())
1031 })
1032 .unwrap();
1033
1034 assert_eq!(id, "42");
1035 assert_eq!(name, "hello");
1036 }
1037
1038 #[test]
1041 fn read_list_wrapped() {
1042 let xml = b"<items><member>a</member><member>b</member></items>";
1043 let settings = Arc::new(XmlCodecSettings::default());
1044 let mut deser = XmlDeserializer::new(xml, settings);
1045
1046 static LIST_MEMBER: Schema = Schema::new_member(
1047 shape_id!("test", "L$member"),
1048 ShapeType::String,
1049 "member",
1050 0,
1051 );
1052 static LIST_SCHEMA: Schema = Schema::new_list(shape_id!("test", "L"), &LIST_MEMBER);
1053
1054 let mut items = Vec::new();
1055 deser
1056 .read_list(&LIST_SCHEMA, &mut |d| {
1057 items.push(d.read_string(&LIST_MEMBER)?);
1058 Ok(())
1059 })
1060 .unwrap();
1061
1062 assert_eq!(items, vec!["a", "b"]);
1063 }
1064
1065 #[test]
1066 fn read_map_wrapped() {
1067 let xml = b"<myMap><entry><key>k1</key><value>v1</value></entry><entry><key>k2</key><value>v2</value></entry></myMap>";
1068 let settings = Arc::new(XmlCodecSettings::default());
1069 let mut deser = XmlDeserializer::new(xml, settings);
1070
1071 static MAP_KEY: Schema =
1072 Schema::new_member(shape_id!("test", "M$key"), ShapeType::String, "key", 0);
1073 static MAP_VALUE: Schema =
1074 Schema::new_member(shape_id!("test", "M$value"), ShapeType::String, "value", 0);
1075 static MAP_SCHEMA: Schema = Schema::new_map(shape_id!("test", "M"), &MAP_KEY, &MAP_VALUE);
1076
1077 let mut entries = Vec::new();
1078 deser
1079 .read_map(&MAP_SCHEMA, &mut |k, d| {
1080 entries.push((k, d.read_string(&MAP_VALUE)?));
1081 Ok(())
1082 })
1083 .unwrap();
1084
1085 assert_eq!(
1086 entries,
1087 vec![
1088 ("k1".to_owned(), "v1".to_owned()),
1089 ("k2".to_owned(), "v2".to_owned())
1090 ]
1091 );
1092 }
1093
1094 #[test]
1095 fn read_map_with_renamed_key_value() {
1096 let xml = b"<m><entry><Attribute>a</Attribute><Setting>s</Setting></entry></m>";
1097 let settings = Arc::new(XmlCodecSettings::default());
1098 let mut deser = XmlDeserializer::new(xml, settings);
1099
1100 static MAP_KEY: Schema =
1101 Schema::new_member(shape_id!("test", "M$key"), ShapeType::String, "key", 0)
1102 .with_xml_name("Attribute");
1103 static MAP_VALUE: Schema =
1104 Schema::new_member(shape_id!("test", "M$value"), ShapeType::String, "value", 0)
1105 .with_xml_name("Setting");
1106 static MAP_SCHEMA: Schema = Schema::new_map(shape_id!("test", "M"), &MAP_KEY, &MAP_VALUE);
1107
1108 let mut entries = Vec::new();
1109 deser
1110 .read_map(&MAP_SCHEMA, &mut |k, d| {
1111 entries.push((k, d.read_string(&MAP_VALUE)?));
1112 Ok(())
1113 })
1114 .unwrap();
1115
1116 assert_eq!(entries, vec![("a".to_owned(), "s".to_owned())]);
1117 }
1118
1119 #[test]
1122 fn read_struct_flattened_list() {
1123 let xml = b"<S><name>hi</name><item>a</item><item>b</item></S>";
1125 let settings = Arc::new(XmlCodecSettings::default());
1126 let mut deser = XmlDeserializer::new(xml, settings);
1127
1128 static S_NAME: Schema =
1129 Schema::new_member(shape_id!("test", "S$name"), ShapeType::String, "name", 0);
1130 static S_ITEMS: Schema =
1131 Schema::new_member(shape_id!("test", "S$items"), ShapeType::List, "items", 1)
1132 .with_xml_flattened()
1133 .with_xml_name("item");
1134 static S_SCHEMA: Schema = Schema::new_struct(
1135 shape_id!("test", "S"),
1136 ShapeType::Structure,
1137 &[&S_NAME, &S_ITEMS],
1138 );
1139
1140 let mut name = String::new();
1141 let mut items = Vec::new();
1142 deser
1143 .read_struct(&S_SCHEMA, &mut |member, d| {
1144 match member.member_name().unwrap() {
1145 "name" => name = d.read_string(member)?,
1146 "items" => d.read_list(member, &mut |d| {
1147 items.push(d.read_string(member)?);
1148 Ok(())
1149 })?,
1150 _ => {}
1151 }
1152 Ok(())
1153 })
1154 .unwrap();
1155
1156 assert_eq!(name, "hi");
1157 assert_eq!(items, vec!["a", "b"]);
1158 }
1159
1160 #[test]
1161 fn read_struct_flattened_list_intermixed() {
1162 let xml = b"<S><item>x</item><name>n</name><item>y</item></S>";
1164 let settings = Arc::new(XmlCodecSettings::default());
1165 let mut deser = XmlDeserializer::new(xml, settings);
1166
1167 static S_NAME: Schema =
1168 Schema::new_member(shape_id!("test", "S$name"), ShapeType::String, "name", 0);
1169 static S_ITEMS: Schema =
1170 Schema::new_member(shape_id!("test", "S$items"), ShapeType::List, "items", 1)
1171 .with_xml_flattened()
1172 .with_xml_name("item");
1173 static S_SCHEMA: Schema = Schema::new_struct(
1174 shape_id!("test", "S"),
1175 ShapeType::Structure,
1176 &[&S_NAME, &S_ITEMS],
1177 );
1178
1179 let mut name = String::new();
1180 let mut items = Vec::new();
1181 deser
1182 .read_struct(&S_SCHEMA, &mut |member, d| {
1183 match member.member_name().unwrap() {
1184 "name" => name = d.read_string(member)?,
1185 "items" => d.read_list(member, &mut |d| {
1186 items.push(d.read_string(member)?);
1187 Ok(())
1188 })?,
1189 _ => {}
1190 }
1191 Ok(())
1192 })
1193 .unwrap();
1194
1195 assert_eq!(name, "n");
1196 assert_eq!(items, vec!["x", "y"]);
1197 }
1198
1199 #[test]
1202 fn read_scalars() {
1203 let settings = Arc::new(XmlCodecSettings::default());
1204
1205 let mut d = XmlDeserializer::from_text(Cow::Borrowed("true"), settings.clone());
1206 assert!(d.read_boolean(&STRING_MEMBER).unwrap());
1207
1208 let mut d = XmlDeserializer::from_text(Cow::Borrowed("-42"), settings.clone());
1209 assert_eq!(d.read_integer(&STRING_MEMBER).unwrap(), -42);
1210
1211 let mut d = XmlDeserializer::from_text(Cow::Borrowed("NaN"), settings.clone());
1212 assert!(d.read_float(&STRING_MEMBER).unwrap().is_nan());
1213
1214 let mut d = XmlDeserializer::from_text(Cow::Borrowed("Infinity"), settings.clone());
1215 assert_eq!(d.read_double(&STRING_MEMBER).unwrap(), f64::INFINITY);
1216
1217 let mut d = XmlDeserializer::from_text(Cow::Borrowed("aGVsbG8="), settings.clone());
1218 assert_eq!(d.read_blob(&STRING_MEMBER).unwrap().as_ref(), b"hello");
1219
1220 let mut d =
1221 XmlDeserializer::from_text(Cow::Borrowed("2023-04-01T12:00:00Z"), settings.clone());
1222 let ts = d.read_timestamp(&STRING_MEMBER).unwrap();
1223 assert_eq!(ts.secs(), 1680350400);
1224 }
1225
1226 #[test]
1227 fn read_document_returns_error() {
1228 let settings = Arc::new(XmlCodecSettings::default());
1229 let mut deser = XmlDeserializer::from_text(Cow::Borrowed("x"), settings);
1230 assert_eq!(
1231 deser.read_document(&STRING_MEMBER).unwrap_err().to_string(),
1232 "document types are not supported by REST XML"
1233 );
1234 }
1235
1236 #[test]
1247 fn read_struct_empty_body_errors() {
1248 let settings = Arc::new(XmlCodecSettings::default());
1249 let mut deser = XmlDeserializer::new(b"", settings);
1250 let err = deser
1251 .read_struct(&PERSON_SCHEMA, &mut |_member, _d| Ok(()))
1252 .expect_err("empty body must be rejected by read_struct");
1253 let _ = format!("{err}");
1254 }
1255
1256 #[test]
1257 fn read_list_empty_body_errors() {
1258 let settings = Arc::new(XmlCodecSettings::default());
1259 let mut deser = XmlDeserializer::new(b"", settings);
1260 deser
1261 .read_list(&PERSON_SCHEMA, &mut |_d| Ok(()))
1262 .expect_err("empty body must be rejected by read_list");
1263 }
1264
1265 #[test]
1266 fn read_map_empty_body_errors() {
1267 let settings = Arc::new(XmlCodecSettings::default());
1268 let mut deser = XmlDeserializer::new(b"", settings);
1269 deser
1270 .read_map(&PERSON_SCHEMA, &mut |_k, _d| Ok(()))
1271 .expect_err("empty body must be rejected by read_map");
1272 }
1273
1274 fn settings_with_max_depth(n: u32) -> Arc<XmlCodecSettings> {
1280 Arc::new(XmlCodecSettings::builder().max_depth(n).build())
1281 }
1282
1283 fn nested_struct_xml(depth: u32) -> Vec<u8> {
1285 let mut s = String::new();
1286 for _ in 0..depth {
1287 s.push_str("<r>");
1288 }
1289 s.push('v');
1290 for _ in 0..depth {
1291 s.push_str("</r>");
1292 }
1293 s.into_bytes()
1294 }
1295
1296 #[test]
1297 fn read_struct_rejects_overdeep_payloads() {
1298 static R_MEMBER_SELF: Schema =
1302 Schema::new_member(shape_id!("test", "R$r"), ShapeType::Structure, "r", 0);
1303 static R_SCHEMA: Schema = Schema::new_struct(
1304 shape_id!("test", "R"),
1305 ShapeType::Structure,
1306 &[&R_MEMBER_SELF],
1307 );
1308
1309 let max = 4;
1311 let xml = nested_struct_xml(max + 2);
1312 let mut deser = XmlDeserializer::new(&xml, settings_with_max_depth(max));
1313
1314 fn consume(_m: &Schema, d: &mut dyn ShapeDeserializer) -> Result<(), SerdeError> {
1316 d.read_struct(&R_SCHEMA, &mut consume)
1317 }
1318 let err = deser
1319 .read_struct(&R_SCHEMA, &mut consume)
1320 .expect_err("must reject payload exceeding max_depth");
1321 assert!(
1322 format!("{err}").contains("maximum nesting depth exceeded"),
1323 "expected depth-exceeded error, got: {err}"
1324 );
1325 }
1326
1327 #[test]
1328 fn read_struct_accepts_payloads_up_to_max_depth() {
1329 static R_MEMBER_SELF: Schema =
1334 Schema::new_member(shape_id!("test", "R$r"), ShapeType::Structure, "r", 0);
1335 static R_SCHEMA: Schema = Schema::new_struct(
1336 shape_id!("test", "R"),
1337 ShapeType::Structure,
1338 &[&R_MEMBER_SELF],
1339 );
1340
1341 let max = 4;
1345 let xml = nested_struct_xml(max);
1346 let mut deser = XmlDeserializer::new(&xml, settings_with_max_depth(max));
1347
1348 let mut depth_seen = 0u32;
1349 fn consume(
1350 _m: &Schema,
1351 d: &mut dyn ShapeDeserializer,
1352 depth_seen: &mut u32,
1353 ) -> Result<(), SerdeError> {
1354 *depth_seen += 1;
1355 d.read_struct(&R_SCHEMA, &mut |m, d2| consume(m, d2, depth_seen))
1356 }
1357
1358 deser
1359 .read_struct(&R_SCHEMA, &mut |m, d| consume(m, d, &mut depth_seen))
1360 .expect("payload at exactly max_depth must succeed");
1361 }
1362
1363 #[test]
1364 fn read_list_rejects_overdeep_payloads() {
1365 static L_MEMBER: Schema =
1367 Schema::new_member(shape_id!("test", "L$member"), ShapeType::List, "member", 0);
1368 static L_SCHEMA: Schema = Schema::new_list(shape_id!("test", "L"), &L_MEMBER);
1369
1370 let max = 3;
1371 let xml = b"<l><l><l><l><l/></l></l></l></l>";
1373 let mut deser = XmlDeserializer::new(xml, settings_with_max_depth(max));
1374
1375 fn consume(d: &mut dyn ShapeDeserializer) -> Result<(), SerdeError> {
1376 d.read_list(&L_SCHEMA, &mut consume)
1377 }
1378 let err = deser
1379 .read_list(&L_SCHEMA, &mut consume)
1380 .expect_err("nested-list payload exceeding max_depth must error");
1381 assert!(
1382 format!("{err}").contains("maximum nesting depth exceeded"),
1383 "expected depth-exceeded error, got: {err}"
1384 );
1385 }
1386
1387 #[test]
1388 fn depth_resets_between_sibling_reads() {
1389 static R_MEMBER_SELF: Schema =
1392 Schema::new_member(shape_id!("test", "R$r"), ShapeType::Structure, "r", 0);
1393 static R_SCHEMA: Schema = Schema::new_struct(
1394 shape_id!("test", "R"),
1395 ShapeType::Structure,
1396 &[&R_MEMBER_SELF],
1397 );
1398
1399 let max = 4;
1400 let xml = nested_struct_xml(2);
1401 let mut deser = XmlDeserializer::new(&xml, settings_with_max_depth(max));
1402
1403 deser
1405 .read_struct(&R_SCHEMA, &mut |_m, d| {
1406 d.read_struct(&R_SCHEMA, &mut |_, _| Ok(()))
1407 })
1408 .expect("first read at depth=2 must succeed");
1409
1410 deser
1413 .read_struct(&R_SCHEMA, &mut |_m, d| {
1414 d.read_struct(&R_SCHEMA, &mut |_, _| Ok(()))
1415 })
1416 .expect("sibling read on the same deserializer must succeed");
1417 }
1418
1419 #[test]
1420 fn depth_resets_after_consumer_error() {
1421 static L_MEMBER: Schema = Schema::new_member(
1434 shape_id!("test", "L$member"),
1435 ShapeType::String,
1436 "member",
1437 0,
1438 );
1439 static L_SCHEMA: Schema = Schema::new_list(shape_id!("test", "L"), &L_MEMBER);
1440
1441 let max = 2;
1447 let xml = b"<l><member>a</member><member>b</member></l>";
1448 let mut deser = XmlDeserializer::new(xml, settings_with_max_depth(max));
1449
1450 let mut force_err = |_: &mut dyn ShapeDeserializer| -> Result<(), SerdeError> {
1452 Err(SerdeError::custom("forced"))
1453 };
1454 deser
1455 .read_list(&L_SCHEMA, &mut force_err)
1456 .expect_err("forced consumer error must propagate");
1457
1458 for i in 0..(max as usize + 5) {
1462 deser
1463 .read_list(&L_SCHEMA, &mut force_err)
1464 .expect_err(&format!("forced consumer error must propagate (iter {i})"));
1465 }
1466
1467 let mut count = 0usize;
1470 deser
1471 .read_list(&L_SCHEMA, &mut |d| {
1472 count += 1;
1473 d.read_string(&L_MEMBER).map(|_| ())
1474 })
1475 .expect("subsequent successful read must not be poisoned by prior errors");
1476 assert_eq!(count, 2);
1477 }
1478
1479 #[test]
1480 fn read_struct_unwrapped_output_with_prolog() {
1481 static MEMBER: Schema = Schema::new_member(
1495 shape_id!("test", "U$location"),
1496 ShapeType::String,
1497 "LocationConstraint",
1498 0,
1499 );
1500 static SCHEMA: Schema =
1501 Schema::new_struct(shape_id!("test", "U"), ShapeType::Structure, &[&MEMBER])
1502 .with_xml_unwrapped_output();
1503
1504 let xml = b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<LocationConstraint>us-west-2</LocationConstraint>";
1505 let mut deser = XmlDeserializer::new(xml, Arc::new(XmlCodecSettings::default()));
1506
1507 let mut got = String::new();
1508 deser
1509 .read_struct(&SCHEMA, &mut |member, d| {
1510 got = d.read_string(member)?;
1511 Ok(())
1512 })
1513 .expect("unwrapped output must deserialize through prolog");
1514 assert_eq!(got, "us-west-2");
1515 }
1516
1517 #[test]
1529 fn find_element_slice_handles_multibyte_utf8() {
1530 static KEY_MEMBER: Schema =
1535 Schema::new_member(shape_id!("test", "Wrapper$k"), ShapeType::Map, "entries", 0);
1536 static MAP_SCHEMA: Schema = Schema::new_map(
1537 shape_id!("test", "MyMap"),
1538 &aws_smithy_schema::prelude::STRING,
1539 &aws_smithy_schema::prelude::STRING,
1540 );
1541 static WRAPPER: Schema = Schema::new_struct(
1542 shape_id!("test", "Wrapper"),
1543 ShapeType::Structure,
1544 &[&KEY_MEMBER],
1545 );
1546 let _ = MAP_SCHEMA; let xml =
1549 "<Wrapper><entries><entry><key>Б</key><value></value></entry></entries></Wrapper>";
1550
1551 let settings = Arc::new(XmlCodecSettings::default());
1552 let mut deser = XmlDeserializer::new(xml.as_bytes(), settings);
1553
1554 let mut entries = Vec::new();
1555 deser
1556 .read_struct(&WRAPPER, &mut |member, d| {
1557 d.read_map(member, &mut |k, d| {
1558 entries.push((k, d.read_string(&aws_smithy_schema::prelude::STRING)?));
1559 Ok(())
1560 })
1561 })
1562 .expect("must not panic on multi-byte UTF-8 inside map elements");
1563
1564 assert_eq!(entries, vec![("Б".to_owned(), String::new())]);
1565 }
1566
1567 #[test]
1568 fn find_depth2_by_predicate_wrapped() {
1569 let xml = b"<Resp><FooResult><A>1</A></FooResult><Metadata/></Resp>";
1570 let got = find_depth2_element_slice_by(xml, |n| n.ends_with("Result"));
1571 assert_eq!(got, Some(&b"<FooResult><A>1</A></FooResult>"[..]));
1572 }
1573
1574 #[test]
1575 fn find_depth2_by_predicate_self_closing() {
1576 let xml = b"<Resp><FooResult/><Metadata><Id>r</Id></Metadata></Resp>";
1579 let got = find_depth2_element_slice_by(xml, |n| n.ends_with("Result"));
1580 assert_eq!(got, Some(&b"<FooResult/>"[..]));
1581 }
1582
1583 #[test]
1584 fn find_depth2_by_predicate_root_match() {
1585 let xml = b"<Error><Code>Boom</Code></Error>";
1587 let got = find_depth2_element_slice_by(xml, |n| n == "Error");
1588 assert_eq!(got, Some(&xml[..]));
1589 }
1590
1591 #[test]
1592 fn find_depth2_by_predicate_no_match() {
1593 let xml = b"<Resp><Metadata/></Resp>";
1594 assert_eq!(find_depth2_element_slice_by(xml, |n| n == "Error"), None);
1595 }
1596
1597 #[test]
1598 fn find_depth2_by_predicate_invalid_xml() {
1599 assert_eq!(find_depth2_element_slice_by(b"not xml", |_| true), None);
1600 }
1601
1602 #[test]
1610 fn read_string_list_helper() {
1611 let xml = b"<items><member>a</member><member>b</member><member></member></items>";
1612 let settings = Arc::new(XmlCodecSettings::default());
1613 let mut deser = XmlDeserializer::new(xml, settings);
1614
1615 static LIST_MEMBER: Schema = Schema::new_member(
1616 shape_id!("test", "L$member"),
1617 ShapeType::String,
1618 "member",
1619 0,
1620 );
1621 static LIST_SCHEMA: Schema = Schema::new_list(shape_id!("test", "L"), &LIST_MEMBER);
1622
1623 let out = deser.read_string_list(&LIST_SCHEMA).unwrap();
1624 assert_eq!(out, vec!["a".to_owned(), "b".to_owned(), String::new()]);
1625 }
1626
1627 #[test]
1628 fn read_string_list_helper_renamed_member_name() {
1629 let xml = b"<items><Item>x</Item><Item>y</Item></items>";
1634 let settings = Arc::new(XmlCodecSettings::default());
1635 let mut deser = XmlDeserializer::new(xml, settings);
1636
1637 static LIST_MEMBER: Schema = Schema::new_member(
1638 shape_id!("test", "L$member"),
1639 ShapeType::String,
1640 "member",
1641 0,
1642 );
1643 static LIST_SCHEMA: Schema = Schema::new_list(shape_id!("test", "L"), &LIST_MEMBER);
1644
1645 let out = deser.read_string_list(&LIST_SCHEMA).unwrap();
1646 assert_eq!(out, vec!["x".to_owned(), "y".to_owned()]);
1647 }
1648
1649 #[test]
1650 fn read_blob_list_helper() {
1651 let xml = b"<blobs><member>aGVsbG8=</member><member>d29ybGQ=</member></blobs>";
1653 let settings = Arc::new(XmlCodecSettings::default());
1654 let mut deser = XmlDeserializer::new(xml, settings);
1655
1656 static LIST_MEMBER: Schema =
1657 Schema::new_member(shape_id!("test", "B$member"), ShapeType::Blob, "member", 0);
1658 static LIST_SCHEMA: Schema = Schema::new_list(shape_id!("test", "B"), &LIST_MEMBER);
1659
1660 let out = deser.read_blob_list(&LIST_SCHEMA).unwrap();
1661 assert_eq!(out.len(), 2);
1662 assert_eq!(out[0].as_ref(), b"hello");
1663 assert_eq!(out[1].as_ref(), b"world");
1664 }
1665
1666 #[test]
1667 fn read_blob_list_helper_rejects_invalid_base64() {
1668 let xml = b"<blobs><member>!!!not-base64!!!</member></blobs>";
1669 let settings = Arc::new(XmlCodecSettings::default());
1670 let mut deser = XmlDeserializer::new(xml, settings);
1671
1672 static LIST_MEMBER: Schema =
1673 Schema::new_member(shape_id!("test", "B$member"), ShapeType::Blob, "member", 0);
1674 static LIST_SCHEMA: Schema = Schema::new_list(shape_id!("test", "B"), &LIST_MEMBER);
1675
1676 let err = deser.read_blob_list(&LIST_SCHEMA).unwrap_err();
1677 assert!(format!("{err}").contains("base64"));
1678 }
1679
1680 #[test]
1681 fn read_integer_list_helper() {
1682 let xml = b"<nums><member>1</member><member>-42</member><member>0</member></nums>";
1683 let settings = Arc::new(XmlCodecSettings::default());
1684 let mut deser = XmlDeserializer::new(xml, settings);
1685
1686 static LIST_MEMBER: Schema = Schema::new_member(
1687 shape_id!("test", "I$member"),
1688 ShapeType::Integer,
1689 "member",
1690 0,
1691 );
1692 static LIST_SCHEMA: Schema = Schema::new_list(shape_id!("test", "I"), &LIST_MEMBER);
1693
1694 let out = deser.read_integer_list(&LIST_SCHEMA).unwrap();
1695 assert_eq!(out, vec![1i32, -42, 0]);
1696 }
1697
1698 #[test]
1699 fn read_long_list_helper() {
1700 let xml = b"<nums><member>9223372036854775807</member><member>-1</member></nums>";
1701 let settings = Arc::new(XmlCodecSettings::default());
1702 let mut deser = XmlDeserializer::new(xml, settings);
1703
1704 static LIST_MEMBER: Schema =
1705 Schema::new_member(shape_id!("test", "Lo$member"), ShapeType::Long, "member", 0);
1706 static LIST_SCHEMA: Schema = Schema::new_list(shape_id!("test", "Lo"), &LIST_MEMBER);
1707
1708 let out = deser.read_long_list(&LIST_SCHEMA).unwrap();
1709 assert_eq!(out, vec![i64::MAX, -1]);
1710 }
1711
1712 #[test]
1713 fn read_string_string_map_helper() {
1714 let xml = b"<m><entry><key>a</key><value>1</value></entry>\
1715 <entry><key>b</key><value>2</value></entry></m>";
1716 let settings = Arc::new(XmlCodecSettings::default());
1717 let mut deser = XmlDeserializer::new(xml, settings);
1718
1719 static MAP_KEY: Schema =
1720 Schema::new_member(shape_id!("test", "M$key"), ShapeType::String, "key", 0);
1721 static MAP_VALUE: Schema =
1722 Schema::new_member(shape_id!("test", "M$value"), ShapeType::String, "value", 0);
1723 static MAP_SCHEMA: Schema = Schema::new_map(shape_id!("test", "M"), &MAP_KEY, &MAP_VALUE);
1724
1725 let out = deser.read_string_string_map(&MAP_SCHEMA).unwrap();
1726 assert_eq!(out.len(), 2);
1727 assert_eq!(out.get("a").map(String::as_str), Some("1"));
1728 assert_eq!(out.get("b").map(String::as_str), Some("2"));
1729 }
1730
1731 #[test]
1732 fn read_string_string_map_helper_with_renamed_key_value() {
1733 let xml = b"<m><entry><K>a</K><V>1</V></entry></m>";
1736 let settings = Arc::new(XmlCodecSettings::default());
1737 let mut deser = XmlDeserializer::new(xml, settings);
1738
1739 static MAP_KEY: Schema =
1740 Schema::new_member(shape_id!("test", "M2$key"), ShapeType::String, "key", 0)
1741 .with_xml_name("K");
1742 static MAP_VALUE: Schema =
1743 Schema::new_member(shape_id!("test", "M2$value"), ShapeType::String, "value", 0)
1744 .with_xml_name("V");
1745 static MAP_SCHEMA: Schema = Schema::new_map(shape_id!("test", "M2"), &MAP_KEY, &MAP_VALUE);
1746
1747 let out = deser.read_string_string_map(&MAP_SCHEMA).unwrap();
1748 assert_eq!(out.get("a").map(String::as_str), Some("1"));
1749 }
1750
1751 #[test]
1761 fn nested_struct_three_levels_deep() {
1762 static LEAF: Schema =
1763 Schema::new_member(shape_id!("t", "Inner"), ShapeType::String, "Leaf", 0);
1764 static INNER_SCHEMA: Schema =
1765 Schema::new_struct(shape_id!("t", "Inner"), ShapeType::Structure, &[&LEAF]);
1766 static INNER_MEMBER: Schema =
1767 Schema::new_member(shape_id!("t", "Middle"), ShapeType::Structure, "Inner", 0);
1768 static MIDDLE_SCHEMA: Schema = Schema::new_struct(
1769 shape_id!("t", "Middle"),
1770 ShapeType::Structure,
1771 &[&INNER_MEMBER],
1772 );
1773 static MIDDLE_MEMBER: Schema =
1774 Schema::new_member(shape_id!("t", "Outer"), ShapeType::Structure, "Middle", 0);
1775 static OUTER_SCHEMA: Schema = Schema::new_struct(
1776 shape_id!("t", "Outer"),
1777 ShapeType::Structure,
1778 &[&MIDDLE_MEMBER],
1779 );
1780 static OUTER_MEMBER: Schema =
1781 Schema::new_member(shape_id!("t", "Root"), ShapeType::Structure, "Outer", 0);
1782 static ROOT: Schema = Schema::new_struct(
1783 shape_id!("t", "Root"),
1784 ShapeType::Structure,
1785 &[&OUTER_MEMBER],
1786 );
1787
1788 let xml = b"<Root><Outer><Middle><Inner><Leaf>value</Leaf></Inner></Middle></Outer></Root>";
1789 let settings = Arc::new(XmlCodecSettings::default());
1790 let mut deser = XmlDeserializer::new(xml, settings);
1791 let mut leaf = String::new();
1792 deser
1793 .read_struct(&ROOT, &mut |outer_m, d_outer| {
1794 assert_eq!(outer_m.member_name(), Some("Outer"));
1795 d_outer.read_struct(&OUTER_SCHEMA, &mut |middle_m, d_middle| {
1796 assert_eq!(middle_m.member_name(), Some("Middle"));
1797 d_middle.read_struct(&MIDDLE_SCHEMA, &mut |inner_m, d_inner| {
1798 assert_eq!(inner_m.member_name(), Some("Inner"));
1799 d_inner.read_struct(&INNER_SCHEMA, &mut |leaf_m, d_leaf| {
1800 if leaf_m.member_name() == Some("Leaf") {
1801 leaf = d_leaf.read_string(leaf_m)?;
1802 }
1803 Ok(())
1804 })
1805 })
1806 })
1807 })
1808 .expect("3-level nested struct should round-trip");
1809 assert_eq!(leaf, "value");
1810 }
1811
1812 #[test]
1813 fn struct_member_with_escaped_text_round_trips() {
1814 static VALUE: Schema =
1815 Schema::new_member(shape_id!("t", "Body"), ShapeType::String, "value", 0);
1816 static BODY_SCHEMA: Schema =
1817 Schema::new_struct(shape_id!("t", "Body"), ShapeType::Structure, &[&VALUE]);
1818 static PAYLOAD_MEMBER: Schema = Schema::new_member(
1819 shape_id!("t", "Envelope"),
1820 ShapeType::Structure,
1821 "payload",
1822 0,
1823 );
1824 static ENVELOPE_SCHEMA: Schema = Schema::new_struct(
1825 shape_id!("t", "Envelope"),
1826 ShapeType::Structure,
1827 &[&PAYLOAD_MEMBER],
1828 );
1829
1830 let xml = b"<Envelope><payload><value>foo&bar</value></payload></Envelope>";
1835 let settings = Arc::new(XmlCodecSettings::default());
1836 let mut deser = XmlDeserializer::new(xml, settings);
1837 let mut value_str = String::new();
1838 deser
1839 .read_struct(&ENVELOPE_SCHEMA, &mut |payload_m, d_payload| {
1840 let _ = payload_m;
1841 d_payload.read_struct(&BODY_SCHEMA, &mut |inner_m, d_inner| {
1842 if inner_m.member_name() == Some("value") {
1843 value_str = d_inner.read_string(inner_m)?;
1844 }
1845 Ok(())
1846 })
1847 })
1848 .expect("escaped text inside a nested struct should round-trip");
1849 assert_eq!(value_str, "foo&bar");
1850 }
1851
1852 #[test]
1853 fn read_map_preserves_empty_string_key() {
1854 static KEY: Schema =
1857 Schema::new_member(shape_id!("t", "M$key"), ShapeType::String, "key", 0);
1858 static VALUE: Schema =
1859 Schema::new_member(shape_id!("t", "M$value"), ShapeType::String, "value", 1);
1860 static MAP: Schema = Schema::new_map(shape_id!("t", "M"), &KEY, &VALUE);
1861
1862 let xml = b"<Root><entry><key></key><value>v1</value></entry></Root>";
1863 let settings = Arc::new(XmlCodecSettings::default());
1864 let mut deser = XmlDeserializer::new(xml, settings);
1865
1866 let mut got: std::collections::HashMap<String, String> = Default::default();
1867 deser
1868 .read_map(&MAP, &mut |k, d| {
1869 let v = d.read_string(&VALUE)?;
1870 got.insert(k, v);
1871 Ok(())
1872 })
1873 .expect("empty-key entry should be preserved");
1874 assert_eq!(got.get("").map(String::as_str), Some("v1"));
1875 }
1876}