polyxml 0.33.1__tar.gz → 0.33.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. {polyxml-0.33.1 → polyxml-0.33.2}/Cargo.lock +6 -6
  2. {polyxml-0.33.1 → polyxml-0.33.2}/Cargo.toml +1 -1
  3. {polyxml-0.33.1 → polyxml-0.33.2}/PKG-INFO +1 -1
  4. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/src/codegen/go/mod.rs +26 -0
  5. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/src/parser.rs +205 -0
  6. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/src/schema.rs +8 -1
  7. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/src/schema_parser/mod.rs +71 -7
  8. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/src/serializer.rs +105 -0
  9. polyxml-0.33.2/crates/polyxml-core/tests/test_any_element.rs +154 -0
  10. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/tests/test_python_codegen.rs +31 -0
  11. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-python/src/lib.rs +142 -10
  12. {polyxml-0.33.1 → polyxml-0.33.2}/pyproject.toml +1 -1
  13. {polyxml-0.33.1 → polyxml-0.33.2}/python/polyxml/__init__.py +12 -1
  14. {polyxml-0.33.1 → polyxml-0.33.2}/README.md +0 -0
  15. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/Cargo.toml +0 -0
  16. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/README.md +0 -0
  17. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/benches/core_benchmarks.rs +0 -0
  18. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/benches/support/tag_dispatch_fixtures.rs +0 -0
  19. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/benches/tag_dispatch.rs +0 -0
  20. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/src/codegen/cpp/mod.rs +0 -0
  21. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/src/codegen/csharp/mod.rs +0 -0
  22. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/src/codegen/java/codec.rs +0 -0
  23. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/src/codegen/java/mod.rs +0 -0
  24. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/src/codegen/java/models.rs +0 -0
  25. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/src/codegen/mod.rs +0 -0
  26. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/src/codegen/python/aot.rs +0 -0
  27. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/src/codegen/python/mod.rs +0 -0
  28. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/src/codegen/rust/mod.rs +0 -0
  29. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/src/codegen/typescript/mod.rs +0 -0
  30. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/src/converters.rs +0 -0
  31. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/src/error.rs +0 -0
  32. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/src/ir/chunker.rs +0 -0
  33. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/src/ir/mod.rs +0 -0
  34. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/src/ir/tarjan.rs +0 -0
  35. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/src/json.rs +0 -0
  36. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/src/lib.rs +0 -0
  37. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/src/transcoder.rs +0 -0
  38. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/src/value.rs +0 -0
  39. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/tests/fixtures/lexical_union.xsd +0 -0
  40. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/tests/fixtures/mixed_content.xsd +0 -0
  41. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/tests/fixtures/ts_mixed_content_inheritance.xsd +0 -0
  42. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/tests/test_adversarial_identifiers.rs +0 -0
  43. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/tests/test_any_attribute.rs +0 -0
  44. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/tests/test_conformance.rs +0 -0
  45. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/tests/test_core.rs +0 -0
  46. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/tests/test_cpp_codegen.rs +0 -0
  47. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/tests/test_csharp_codegen.rs +0 -0
  48. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/tests/test_go_codegen.rs +0 -0
  49. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/tests/test_java_codegen.rs +0 -0
  50. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/tests/test_json.rs +0 -0
  51. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/tests/test_lexical_union_codegen.rs +0 -0
  52. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/tests/test_mixed_content.rs +0 -0
  53. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/tests/test_pattern_codegen.rs +0 -0
  54. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/tests/test_rust_codecs.rs +0 -0
  55. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/tests/test_rust_codegen.rs +0 -0
  56. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/tests/test_schema_audit.rs +0 -0
  57. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/tests/test_schema_ir.rs +0 -0
  58. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/tests/test_security.rs +0 -0
  59. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/tests/test_transcoder.rs +0 -0
  60. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/tests/test_ts_codegen.rs +0 -0
  61. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/tests/test_unbounded_choice.rs +0 -0
  62. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-core/tests/test_xsi_type.rs +0 -0
  63. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-python/Cargo.toml +0 -0
  64. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-python/README.md +0 -0
  65. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-python/benches/aot_vs_dataclass.py +0 -0
  66. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-python/tests/test_conformance.py +0 -0
  67. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-python/tests/test_generated_models.py +0 -0
  68. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-python/tests/test_json.py +0 -0
  69. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-python/tests/test_mixed_content.py +0 -0
  70. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-python/tests/test_polyxml.py +0 -0
  71. {polyxml-0.33.1 → polyxml-0.33.2}/crates/polyxml-python/tests/test_xsi_type.py +0 -0
  72. {polyxml-0.33.1 → polyxml-0.33.2}/python/polyxml/AGENT_GUIDE.md +0 -0
  73. {polyxml-0.33.1 → polyxml-0.33.2}/python/polyxml/compat/__init__.py +0 -0
  74. {polyxml-0.33.1 → polyxml-0.33.2}/python/polyxml/compat/xsdata.py +0 -0
  75. {polyxml-0.33.1 → polyxml-0.33.2}/python/polyxml/py.typed +0 -0
@@ -608,7 +608,7 @@ dependencies = [
608
608
 
609
609
  [[package]]
610
610
  name = "polyxml"
611
- version = "0.33.1"
611
+ version = "0.33.2"
612
612
  dependencies = [
613
613
  "criterion",
614
614
  "heck",
@@ -629,14 +629,14 @@ dependencies = [
629
629
 
630
630
  [[package]]
631
631
  name = "polyxml-c"
632
- version = "0.33.1"
632
+ version = "0.33.2"
633
633
  dependencies = [
634
634
  "polyxml",
635
635
  ]
636
636
 
637
637
  [[package]]
638
638
  name = "polyxml-cli"
639
- version = "0.33.1"
639
+ version = "0.33.2"
640
640
  dependencies = [
641
641
  "clap",
642
642
  "glob",
@@ -650,7 +650,7 @@ dependencies = [
650
650
 
651
651
  [[package]]
652
652
  name = "polyxml-js"
653
- version = "0.33.1"
653
+ version = "0.33.2"
654
654
  dependencies = [
655
655
  "napi",
656
656
  "napi-build",
@@ -660,7 +660,7 @@ dependencies = [
660
660
 
661
661
  [[package]]
662
662
  name = "polyxml-python"
663
- version = "0.33.1"
663
+ version = "0.33.2"
664
664
  dependencies = [
665
665
  "lexical-core",
666
666
  "polyxml",
@@ -670,7 +670,7 @@ dependencies = [
670
670
 
671
671
  [[package]]
672
672
  name = "polyxml-wasm"
673
- version = "0.33.1"
673
+ version = "0.33.2"
674
674
  dependencies = [
675
675
  "polyxml",
676
676
  "quick-xml",
@@ -3,7 +3,7 @@ resolver = "2"
3
3
  members = ["crates/polyxml-core", "crates/polyxml-python"]
4
4
 
5
5
  [workspace.package]
6
- version = "0.33.1"
6
+ version = "0.33.2"
7
7
  edition = "2021"
8
8
  rust-version = "1.80"
9
9
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: polyxml
3
- Version: 0.33.1
3
+ Version: 0.33.2
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -447,6 +447,14 @@ impl GoCodegen {
447
447
  self.emit_struct(out, s, ir);
448
448
  }
449
449
  }
450
+
451
+ let has_any_element = ir.emitted_types().any(|t| match t {
452
+ TypeDef::Struct(s) => s.fields.iter().any(|f| f.kind == FieldKind::Any),
453
+ _ => false,
454
+ });
455
+ if has_any_element {
456
+ out.push_str("// AnyElement represents an unmapped wildcard XML element (xs:any).\ntype AnyElement struct {\n XMLName xml.Name\n Attrs []xml.Attr `xml:\",any,attr\" json:\"attrs,omitempty\"`\n Content string `xml:\",innerxml\" json:\"content,omitempty\"`\n}\n\n");
457
+ }
450
458
  }
451
459
 
452
460
  fn emit_simple_type(&self, out: &mut String, simple: &SimpleTypeDef, ir: &SchemaIR) {
@@ -1104,6 +1112,15 @@ impl GoCodegen {
1104
1112
  if f.kind == FieldKind::AnyAttribute {
1105
1113
  return "[]xml.Attr".to_string();
1106
1114
  }
1115
+ if f.kind == FieldKind::Any {
1116
+ if f.cardinality.is_list() {
1117
+ return "[]AnyElement".to_string();
1118
+ } else if f.is_cycle_cut || f.cardinality.is_optional() || f.nillable {
1119
+ return "*AnyElement".to_string();
1120
+ } else {
1121
+ return "AnyElement".to_string();
1122
+ }
1123
+ }
1107
1124
  let base_type = self.context.map_type_ref(&f.type_ref);
1108
1125
 
1109
1126
  if f.cardinality.is_list() {
@@ -1156,6 +1173,15 @@ impl GoCodegen {
1156
1173
  if self.options.backend == GoBackend::Sonic {
1157
1174
  parts.push("sonic:\"-\"".to_string());
1158
1175
  }
1176
+ } else if f.kind == FieldKind::Any {
1177
+ let is_opt = f.cardinality.is_optional() || f.nillable;
1178
+ let tag = if is_opt { "any,omitempty" } else { "any" };
1179
+ if self.options.emit_json_tags {
1180
+ parts.push(format!("json:\"{}\"", tag));
1181
+ }
1182
+ if self.options.backend == GoBackend::Sonic {
1183
+ parts.push(format!("sonic:\"{}\"", tag));
1184
+ }
1159
1185
  } else {
1160
1186
  let is_opt = f.cardinality.is_optional() || f.nillable;
1161
1187
  let raw_json_name = if f.kind == FieldKind::Text {
@@ -256,6 +256,40 @@ fn append_general_ref(
256
256
  Ok(())
257
257
  }
258
258
 
259
+ #[derive(Debug)]
260
+ struct AnyElementFrame {
261
+ qname: String,
262
+ attributes: HashMap<String, PolyValue>,
263
+ text: String,
264
+ children: Vec<PolyValue>,
265
+ }
266
+
267
+ fn resolve_element_qname(
268
+ e: &BytesStart,
269
+ scope: &HashMap<String, String>,
270
+ ) -> (Option<String>, String) {
271
+ let raw = e.name().into_inner();
272
+ let (prefix, local) = match raw.split_once(':') {
273
+ Some((p, l)) => (p, l),
274
+ None => ("", raw),
275
+ };
276
+ let ns = scope.get(prefix).cloned();
277
+ (ns, local.to_string())
278
+ }
279
+
280
+ fn parse_any_element_attrs(e: &BytesStart) -> Result<HashMap<String, PolyValue>> {
281
+ let mut attributes = HashMap::new();
282
+ for attr in e.attributes().flatten() {
283
+ let key = attr.key.as_ref();
284
+ if key == "xmlns" || key.starts_with("xmlns:") {
285
+ continue;
286
+ }
287
+ let unescaped = quick_xml::escape::unescape(attr.value.as_ref())?;
288
+ attributes.insert(key.to_string(), PolyValue::String(unescaped.to_string()));
289
+ }
290
+ Ok(attributes)
291
+ }
292
+
259
293
  pub const DEFAULT_MAX_DEPTH: usize = 256;
260
294
 
261
295
  impl XmlDeserializer {
@@ -324,6 +358,7 @@ impl XmlDeserializer {
324
358
  let mut active_scalar_field: Option<(usize, ScalarType, bool)> = None;
325
359
  let mut active_mixed_scalar: Option<(String, ScalarType)> = None;
326
360
  let mut unknown_depth: usize = 0;
361
+ let mut any_stack: Vec<AnyElementFrame> = Vec::new();
327
362
  let mut text_buf: Vec<u8> = Vec::new();
328
363
  let mut buf = Vec::new();
329
364
 
@@ -339,6 +374,23 @@ impl XmlDeserializer {
339
374
  unknown_depth = 1;
340
375
  continue;
341
376
  }
377
+ if !any_stack.is_empty() {
378
+ let scope = namespace_stack.last().unwrap();
379
+ let (ns, local) = resolve_element_qname(e, scope);
380
+ let qname = if let Some(uri) = ns {
381
+ format!("{{{}}}{}", uri, local)
382
+ } else {
383
+ local
384
+ };
385
+ let attributes = parse_any_element_attrs(e)?;
386
+ any_stack.push(AnyElementFrame {
387
+ qname,
388
+ attributes,
389
+ text: String::new(),
390
+ children: Vec::new(),
391
+ });
392
+ continue;
393
+ }
342
394
  if stack.len() >= max_depth {
343
395
  return Err(PolyXmlError::MaxDepthExceeded {
344
396
  max_depth,
@@ -445,6 +497,44 @@ impl XmlDeserializer {
445
497
  } else {
446
498
  unknown_depth = 1;
447
499
  }
500
+ } else if let Some(any_idx) = current_schema.any_element_field {
501
+ let scope = namespace_stack.last().unwrap();
502
+ let (ns, local) = resolve_element_qname(e, scope);
503
+ let any_field = &current_schema.fields[any_idx];
504
+ let allowed = match any_field.namespace.as_deref() {
505
+ Some("##other") => {
506
+ match (ns.as_deref(), current_schema.namespace.as_deref()) {
507
+ (Some(elem_ns), Some(target_ns)) => elem_ns != target_ns,
508
+ (None, Some(_)) => true,
509
+ (Some(_), None) => true,
510
+ (None, None) => false,
511
+ }
512
+ }
513
+ Some("##targetNamespace") => {
514
+ ns.as_deref() == current_schema.namespace.as_deref()
515
+ }
516
+ Some("##local") => ns.is_none(),
517
+ Some(allowed_ns) if allowed_ns != "##any" => {
518
+ ns.as_deref() == Some(allowed_ns)
519
+ }
520
+ _ => true,
521
+ };
522
+ if allowed {
523
+ let qname = if let Some(uri) = ns {
524
+ format!("{{{}}}{}", uri, local)
525
+ } else {
526
+ local
527
+ };
528
+ let attributes = parse_any_element_attrs(e)?;
529
+ any_stack.push(AnyElementFrame {
530
+ qname,
531
+ attributes,
532
+ text: String::new(),
533
+ children: Vec::new(),
534
+ });
535
+ } else {
536
+ unknown_depth = 1;
537
+ }
448
538
  } else {
449
539
  unknown_depth = 1;
450
540
  }
@@ -456,6 +546,27 @@ impl XmlDeserializer {
456
546
  {
457
547
  continue;
458
548
  }
549
+ if !any_stack.is_empty() {
550
+ let scope = namespace_scope(namespace_stack.last().unwrap(), e);
551
+ let (ns, local) = resolve_element_qname(e, &scope);
552
+ let qname = if let Some(uri) = ns {
553
+ format!("{{{}}}{}", uri, local)
554
+ } else {
555
+ local
556
+ };
557
+ let attributes = parse_any_element_attrs(e)?;
558
+ let mut obj = HashMap::new();
559
+ obj.insert("qname".to_string(), PolyValue::String(qname));
560
+ obj.insert("attributes".to_string(), PolyValue::Object(attributes));
561
+ obj.insert("text".to_string(), PolyValue::String(String::new()));
562
+ obj.insert("children".to_string(), PolyValue::List(Vec::new()));
563
+ any_stack
564
+ .last_mut()
565
+ .unwrap()
566
+ .children
567
+ .push(PolyValue::Object(obj));
568
+ continue;
569
+ }
459
570
  let local_name = e.local_name();
460
571
  let scope = namespace_scope(namespace_stack.last().unwrap(), e);
461
572
  let is_nil = is_nil_element(e);
@@ -543,12 +654,65 @@ impl XmlDeserializer {
543
654
  .unwrap()
544
655
  .push_mixed_item(&branch.variant_name, value);
545
656
  }
657
+ } else if let Some(any_idx) = current_schema.any_element_field {
658
+ let (ns, local) = resolve_element_qname(e, &scope);
659
+ let any_field = &current_schema.fields[any_idx];
660
+ let allowed = match any_field.namespace.as_deref() {
661
+ Some("##other") => {
662
+ match (ns.as_deref(), current_schema.namespace.as_deref()) {
663
+ (Some(elem_ns), Some(target_ns)) => elem_ns != target_ns,
664
+ (None, Some(_)) => true,
665
+ (Some(_), None) => true,
666
+ (None, None) => false,
667
+ }
668
+ }
669
+ Some("##targetNamespace") => {
670
+ ns.as_deref() == current_schema.namespace.as_deref()
671
+ }
672
+ Some("##local") => ns.is_none(),
673
+ Some(allowed_ns) if allowed_ns != "##any" => {
674
+ ns.as_deref() == Some(allowed_ns)
675
+ }
676
+ _ => true,
677
+ };
678
+ if allowed {
679
+ let qname = if let Some(uri) = ns {
680
+ format!("{{{}}}{}", uri, local)
681
+ } else {
682
+ local
683
+ };
684
+ let attributes = parse_any_element_attrs(e)?;
685
+ let mut obj = HashMap::new();
686
+ obj.insert("qname".to_string(), PolyValue::String(qname));
687
+ obj.insert("attributes".to_string(), PolyValue::Object(attributes));
688
+ obj.insert("text".to_string(), PolyValue::String(String::new()));
689
+ obj.insert("children".to_string(), PolyValue::List(Vec::new()));
690
+ let poly_obj = PolyValue::Object(obj);
691
+ let parent_frame = stack.last_mut().unwrap();
692
+ if matches!(any_field.val_type, ValueType::List(_)) {
693
+ parent_frame.push_list_item(any_idx, poly_obj);
694
+ } else {
695
+ parent_frame.values[any_idx] = Some(poly_obj);
696
+ }
697
+ }
546
698
  }
547
699
  }
548
700
  Ok(Event::Text(ref e)) => {
549
701
  if unknown_depth > 0 {
550
702
  continue;
551
703
  }
704
+ if !any_stack.is_empty() {
705
+ let raw = e.as_ref();
706
+ let raw_bytes = raw.as_bytes();
707
+ if memchr::memchr(b'&', raw_bytes).is_none() {
708
+ if let Ok(s) = std::str::from_utf8(raw_bytes) {
709
+ any_stack.last_mut().unwrap().text.push_str(s);
710
+ }
711
+ } else if let Ok(unescaped) = quick_xml::escape::unescape(raw) {
712
+ any_stack.last_mut().unwrap().text.push_str(&unescaped);
713
+ }
714
+ continue;
715
+ }
552
716
  let raw = e.as_ref();
553
717
  let raw_bytes = raw.as_bytes();
554
718
  if memchr::memchr(b'&', raw_bytes).is_none() {
@@ -578,6 +742,12 @@ impl XmlDeserializer {
578
742
  if unknown_depth > 0 {
579
743
  continue;
580
744
  }
745
+ if !any_stack.is_empty() {
746
+ if let Ok(s) = std::str::from_utf8(e.as_ref().as_bytes()) {
747
+ any_stack.last_mut().unwrap().text.push_str(s);
748
+ }
749
+ continue;
750
+ }
581
751
  if active_scalar_field.is_some() || active_mixed_scalar.is_some() {
582
752
  text_buf.extend_from_slice(e.as_ref().as_bytes());
583
753
  } else if let Some(frame) = stack.last_mut() {
@@ -592,6 +762,14 @@ impl XmlDeserializer {
592
762
  if unknown_depth > 0 {
593
763
  continue;
594
764
  }
765
+ if !any_stack.is_empty() {
766
+ let mut resolved = Vec::new();
767
+ append_general_ref(e, true, &mut resolved, None)?;
768
+ if let Ok(s) = std::str::from_utf8(&resolved) {
769
+ any_stack.last_mut().unwrap().text.push_str(s);
770
+ }
771
+ continue;
772
+ }
595
773
  let frame_tb = stack.last_mut().and_then(|f| f.frame_text_buf.as_mut());
596
774
  if active_scalar_field.is_some()
597
775
  || active_mixed_scalar.is_some()
@@ -617,6 +795,33 @@ impl XmlDeserializer {
617
795
  unknown_depth -= 1;
618
796
  continue;
619
797
  }
798
+ if !any_stack.is_empty() {
799
+ let frame = any_stack.pop().unwrap();
800
+ let mut obj = HashMap::new();
801
+ obj.insert("qname".to_string(), PolyValue::String(frame.qname));
802
+ obj.insert(
803
+ "attributes".to_string(),
804
+ PolyValue::Object(frame.attributes),
805
+ );
806
+ obj.insert("text".to_string(), PolyValue::String(frame.text));
807
+ obj.insert("children".to_string(), PolyValue::List(frame.children));
808
+ let poly_obj = PolyValue::Object(obj);
809
+
810
+ if let Some(parent) = any_stack.last_mut() {
811
+ parent.children.push(poly_obj);
812
+ } else {
813
+ let parent_frame = stack.last_mut().unwrap();
814
+ if let Some(any_idx) = parent_frame.schema.any_element_field {
815
+ let field = &parent_frame.schema.fields[any_idx];
816
+ if matches!(field.val_type, ValueType::List(_)) {
817
+ parent_frame.push_list_item(any_idx, poly_obj);
818
+ } else {
819
+ parent_frame.values[any_idx] = Some(poly_obj);
820
+ }
821
+ }
822
+ }
823
+ continue;
824
+ }
620
825
  if let Some((kind, scalar_type)) = active_mixed_scalar.take() {
621
826
  let parsed = ValueConverter::parse_scalar(&scalar_type, &text_buf, &kind)?;
622
827
  stack.last_mut().unwrap().push_mixed_item(&kind, parsed);
@@ -7,6 +7,7 @@ pub enum FieldKind {
7
7
  Element,
8
8
  Text,
9
9
  AnyAttribute,
10
+ Any,
10
11
  }
11
12
 
12
13
  #[derive(Debug, Clone, PartialEq, Eq)]
@@ -108,6 +109,7 @@ pub struct ModelSchema {
108
109
  pub attribute_map: HashMap<Vec<u8>, usize>,
109
110
  pub text_field: Option<usize>,
110
111
  pub any_attribute_field: Option<usize>,
112
+ pub any_element_field: Option<usize>,
111
113
  pub mixed_content: Option<MixedContentSchema>,
112
114
  /// Declared `abstract="true"` in the source schema.
113
115
  pub is_abstract: bool,
@@ -338,7 +340,7 @@ impl ModelSchema {
338
340
  crate::ir::FieldKind::Element => FieldKind::Element,
339
341
  crate::ir::FieldKind::Text => FieldKind::Text,
340
342
  crate::ir::FieldKind::AnyAttribute => FieldKind::AnyAttribute,
341
- _ => FieldKind::Element,
343
+ crate::ir::FieldKind::Any => FieldKind::Any,
342
344
  };
343
345
 
344
346
  let mut val_type = build_type(&f.type_ref, ir, visited);
@@ -569,6 +571,7 @@ impl ModelSchemaBuilder {
569
571
  let mut attribute_map = HashMap::new();
570
572
  let mut text_field = None;
571
573
  let mut any_attribute_field = None;
574
+ let mut any_element_field = None;
572
575
 
573
576
  for (idx, field) in self.fields.iter().enumerate() {
574
577
  match field.kind {
@@ -591,6 +594,9 @@ impl ModelSchemaBuilder {
591
594
  FieldKind::AnyAttribute => {
592
595
  any_attribute_field = Some(idx);
593
596
  }
597
+ FieldKind::Any => {
598
+ any_element_field = Some(idx);
599
+ }
594
600
  }
595
601
  }
596
602
 
@@ -607,6 +613,7 @@ impl ModelSchemaBuilder {
607
613
  attribute_map,
608
614
  text_field,
609
615
  any_attribute_field,
616
+ any_element_field,
610
617
  mixed_content: None,
611
618
  is_abstract: self.is_abstract,
612
619
  variants: Arc::new(RwLock::new(Vec::new())),
@@ -631,7 +631,26 @@ impl XsdParser {
631
631
  }
632
632
  }
633
633
  "any" => {
634
- fields.push(parse_any_field(e));
634
+ let in_unbounded = compositor_stack.iter().any(|c| c.is_unbounded);
635
+ let in_choice = compositor_stack
636
+ .last()
637
+ .map(|c| c.kind == CompositorKind::Choice)
638
+ .unwrap_or(false);
639
+ let field = parse_any_field(e, in_choice, in_unbounded);
640
+ if in_choice {
641
+ if let Some(frame) = compositor_stack.last_mut() {
642
+ if frame.kind == CompositorKind::Choice {
643
+ frame.choice_branches.push(UnionBranch {
644
+ variant_name: field.name.clone(),
645
+ xml_name: field.xml_name.clone(),
646
+ namespace: field.namespace.clone(),
647
+ type_ref: field.type_ref.clone(),
648
+ documentation: field.documentation.clone(),
649
+ });
650
+ }
651
+ }
652
+ }
653
+ fields.push(field);
635
654
  }
636
655
  "anyAttribute"
637
656
  if !fields.iter().any(|f| f.kind == FieldKind::AnyAttribute) =>
@@ -696,7 +715,26 @@ impl XsdParser {
696
715
  }
697
716
  }
698
717
  "any" => {
699
- fields.push(parse_any_field(e));
718
+ let in_unbounded = compositor_stack.iter().any(|c| c.is_unbounded);
719
+ let in_choice = compositor_stack
720
+ .last()
721
+ .map(|c| c.kind == CompositorKind::Choice)
722
+ .unwrap_or(false);
723
+ let field = parse_any_field(e, in_choice, in_unbounded);
724
+ if in_choice {
725
+ if let Some(frame) = compositor_stack.last_mut() {
726
+ if frame.kind == CompositorKind::Choice {
727
+ frame.choice_branches.push(UnionBranch {
728
+ variant_name: field.name.clone(),
729
+ xml_name: field.xml_name.clone(),
730
+ namespace: field.namespace.clone(),
731
+ type_ref: field.type_ref.clone(),
732
+ documentation: field.documentation.clone(),
733
+ });
734
+ }
735
+ }
736
+ }
737
+ fields.push(field);
700
738
  }
701
739
  "anyAttribute"
702
740
  if !fields.iter().any(|f| f.kind == FieldKind::AnyAttribute) =>
@@ -956,7 +994,10 @@ impl XsdParser {
956
994
  skip_subtree(reader)?;
957
995
  depth -= 1;
958
996
  }
959
- "any" => def.fields.push(parse_any_field(e)),
997
+ "any" => {
998
+ let in_unbounded = compositor_stack.iter().any(|&b| b);
999
+ def.fields.push(parse_any_field(e, in_choice, in_unbounded));
1000
+ }
960
1001
  _ => {}
961
1002
  }
962
1003
  }
@@ -979,7 +1020,10 @@ impl XsdParser {
979
1020
  ));
980
1021
  }
981
1022
  }
982
- "any" => def.fields.push(parse_any_field(e)),
1023
+ "any" => {
1024
+ let in_unbounded = compositor_stack.iter().any(|&b| b);
1025
+ def.fields.push(parse_any_field(e, in_choice, in_unbounded));
1026
+ }
983
1027
  _ => {}
984
1028
  }
985
1029
  }
@@ -1863,14 +1907,34 @@ fn parse_attribute_field(
1863
1907
  })
1864
1908
  }
1865
1909
 
1866
- fn parse_any_field(_e: &BytesStart) -> FieldDef {
1910
+ fn parse_any_field(e: &BytesStart, in_choice: bool, in_unbounded_compositor: bool) -> FieldDef {
1911
+ let min_occurs = if in_choice {
1912
+ 0
1913
+ } else {
1914
+ get_attr_value(e, "minOccurs")
1915
+ .and_then(|v| v.parse::<usize>().ok())
1916
+ .unwrap_or(1)
1917
+ };
1918
+ let max_occurs = if in_unbounded_compositor {
1919
+ OccursLimit::Unbounded
1920
+ } else {
1921
+ match get_attr_value(e, "maxOccurs").as_deref() {
1922
+ Some("unbounded") => OccursLimit::Unbounded,
1923
+ Some(v) => OccursLimit::Count(v.parse::<usize>().unwrap_or(1)),
1924
+ None => OccursLimit::Count(1),
1925
+ }
1926
+ };
1927
+ let namespace = get_attr_value(e, "namespace");
1867
1928
  FieldDef {
1868
1929
  name: "any".to_string(),
1869
1930
  xml_name: "*".to_string(),
1870
- namespace: None,
1931
+ namespace,
1871
1932
  kind: FieldKind::Any,
1872
1933
  type_ref: TypeRef::Primitive(PrimitiveType::AnyType),
1873
- cardinality: Cardinality::unbounded(0),
1934
+ cardinality: Cardinality {
1935
+ min_occurs,
1936
+ max_occurs,
1937
+ },
1874
1938
  nillable: false,
1875
1939
  default_value: None,
1876
1940
  fixed_value: None,
@@ -633,6 +633,10 @@ impl XmlSerializer {
633
633
  }
634
634
  }
635
635
  }
636
+ } else if field.kind == FieldKind::Any {
637
+ if let Some(val) = get_field(idx, &field.name) {
638
+ Self::write_any_wildcard_element(writer, val, ns_ctx)?;
639
+ }
636
640
  }
637
641
  }
638
642
  }
@@ -644,4 +648,105 @@ impl XmlSerializer {
644
648
 
645
649
  Ok(())
646
650
  }
651
+
652
+ fn write_any_wildcard_element<W: std::io::Write>(
653
+ writer: &mut quick_xml::Writer<W>,
654
+ val: &PolyValue,
655
+ ns_ctx: Option<&NamespaceContext>,
656
+ ) -> Result<()> {
657
+ match val {
658
+ PolyValue::List(items) => {
659
+ for item in items {
660
+ Self::write_single_wildcard_element(writer, item, ns_ctx)?;
661
+ }
662
+ }
663
+ PolyValue::Object(_) => {
664
+ Self::write_single_wildcard_element(writer, val, ns_ctx)?;
665
+ }
666
+ _ => {}
667
+ }
668
+ Ok(())
669
+ }
670
+
671
+ fn write_single_wildcard_element<W: std::io::Write>(
672
+ writer: &mut quick_xml::Writer<W>,
673
+ val: &PolyValue,
674
+ ns_ctx: Option<&NamespaceContext>,
675
+ ) -> Result<()> {
676
+ let PolyValue::Object(map) = val else {
677
+ return Ok(());
678
+ };
679
+ let Some(PolyValue::String(qname)) = map.get("qname") else {
680
+ return Ok(());
681
+ };
682
+ let (ns_uri, local) = if qname.starts_with('{') {
683
+ if let Some(end) = qname.find('}') {
684
+ (Some(&qname[1..end]), &qname[end + 1..])
685
+ } else {
686
+ (None, qname.as_str())
687
+ }
688
+ } else {
689
+ (None, qname.as_str())
690
+ };
691
+
692
+ let (qualified, xmlns_attr) = if let Some(uri) = ns_uri {
693
+ if let Some(ctx) = ns_ctx {
694
+ if let Some(prefix) = ctx.uri_to_prefix.get(uri) {
695
+ if !prefix.is_empty() {
696
+ (Cow::Owned(format!("{}:{}", prefix, local)), None)
697
+ } else {
698
+ (Cow::Borrowed(local), None)
699
+ }
700
+ } else {
701
+ (Cow::Borrowed(local), Some(uri))
702
+ }
703
+ } else {
704
+ (Cow::Borrowed(local), Some(uri))
705
+ }
706
+ } else {
707
+ (Cow::Borrowed(local), None)
708
+ };
709
+
710
+ let mut start = BytesStart::new(qualified.as_ref());
711
+ if let Some(uri) = xmlns_attr {
712
+ start.push_attribute(("xmlns", uri));
713
+ }
714
+
715
+ if let Some(PolyValue::Object(attrs)) = map.get("attributes") {
716
+ let mut sorted_keys: Vec<_> = attrs.keys().collect();
717
+ sorted_keys.sort();
718
+ for k in sorted_keys {
719
+ if let Some(v) = attrs.get(k) {
720
+ let mut buf = [0u8; lexical_core::BUFFER_SIZE];
721
+ if let Some(s) = Self::format_scalar_to(v, &mut buf) {
722
+ start.push_attribute((k.as_str(), s));
723
+ }
724
+ }
725
+ }
726
+ }
727
+
728
+ writer
729
+ .write_event(Event::Start(start))
730
+ .map_err(|e| PolyXmlError::SerializationError(e.to_string()))?;
731
+
732
+ if let Some(PolyValue::String(text)) = map.get("text") {
733
+ if !text.is_empty() {
734
+ writer
735
+ .write_event(Event::Text(BytesText::new(text)))
736
+ .map_err(|e| PolyXmlError::SerializationError(e.to_string()))?;
737
+ }
738
+ }
739
+
740
+ if let Some(PolyValue::List(children)) = map.get("children") {
741
+ for child in children {
742
+ Self::write_single_wildcard_element(writer, child, ns_ctx)?;
743
+ }
744
+ }
745
+
746
+ writer
747
+ .write_event(Event::End(BytesEnd::new(qualified.as_ref())))
748
+ .map_err(|e| PolyXmlError::SerializationError(e.to_string()))?;
749
+
750
+ Ok(())
751
+ }
647
752
  }