polyxml 0.34.3__tar.gz → 0.34.4__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 (84) hide show
  1. {polyxml-0.34.3 → polyxml-0.34.4}/Cargo.lock +6 -6
  2. {polyxml-0.34.3 → polyxml-0.34.4}/Cargo.toml +1 -1
  3. {polyxml-0.34.3 → polyxml-0.34.4}/PKG-INFO +1 -1
  4. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/codegen/csharp/mod.rs +49 -1
  5. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/codegen/rust/mod.rs +78 -8
  6. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_qualified_attributes.rs +43 -0
  7. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_rust_codegen.rs +60 -1
  8. {polyxml-0.34.3 → polyxml-0.34.4}/pyproject.toml +1 -1
  9. {polyxml-0.34.3 → polyxml-0.34.4}/README.md +0 -0
  10. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/Cargo.toml +0 -0
  11. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/README.md +0 -0
  12. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/benches/core_benchmarks.rs +0 -0
  13. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/benches/support/tag_dispatch_fixtures.rs +0 -0
  14. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/benches/tag_dispatch.rs +0 -0
  15. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/codegen/cpp/mod.rs +0 -0
  16. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/codegen/go/mod.rs +0 -0
  17. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/codegen/java/codec.rs +0 -0
  18. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/codegen/java/mod.rs +0 -0
  19. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/codegen/java/models.rs +0 -0
  20. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/codegen/mod.rs +0 -0
  21. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/codegen/python/aot.rs +0 -0
  22. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/codegen/python/mod.rs +0 -0
  23. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/codegen/typescript/mod.rs +0 -0
  24. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/converters.rs +0 -0
  25. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/error.rs +0 -0
  26. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/integer.rs +0 -0
  27. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/ir/chunker.rs +0 -0
  28. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/ir/mod.rs +0 -0
  29. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/ir/particle.rs +0 -0
  30. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/ir/tarjan.rs +0 -0
  31. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/json.rs +0 -0
  32. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/lib.rs +0 -0
  33. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/parser.rs +0 -0
  34. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/schema.rs +0 -0
  35. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/schema_parser/mod.rs +0 -0
  36. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/schema_parser/upa.rs +0 -0
  37. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/serializer.rs +0 -0
  38. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/transcoder.rs +0 -0
  39. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/src/value.rs +0 -0
  40. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/fixtures/lexical_union.xsd +0 -0
  41. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/fixtures/mixed_content.xsd +0 -0
  42. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/fixtures/ts_mixed_content_inheritance.xsd +0 -0
  43. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_adversarial_identifiers.rs +0 -0
  44. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_any_attribute.rs +0 -0
  45. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_any_element.rs +0 -0
  46. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_conformance.rs +0 -0
  47. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_core.rs +0 -0
  48. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_cpp_codegen.rs +0 -0
  49. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_csharp_codegen.rs +0 -0
  50. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_go_codegen.rs +0 -0
  51. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_java_codegen.rs +0 -0
  52. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_json.rs +0 -0
  53. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_lexical_lists.rs +0 -0
  54. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_lexical_union_codegen.rs +0 -0
  55. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_mixed_content.rs +0 -0
  56. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_pattern_codegen.rs +0 -0
  57. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_python_codegen.rs +0 -0
  58. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_rust_codecs.rs +0 -0
  59. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_schema_audit.rs +0 -0
  60. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_schema_ir.rs +0 -0
  61. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_security.rs +0 -0
  62. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_temporal_union.rs +0 -0
  63. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_transcoder.rs +0 -0
  64. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_ts_codegen.rs +0 -0
  65. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_ubl_csharp.rs +0 -0
  66. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_unbounded_choice.rs +0 -0
  67. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_upa.rs +0 -0
  68. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_value_constraints.rs +0 -0
  69. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-core/tests/test_xsi_type.rs +0 -0
  70. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-python/Cargo.toml +0 -0
  71. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-python/README.md +0 -0
  72. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-python/benches/aot_vs_dataclass.py +0 -0
  73. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-python/src/lib.rs +0 -0
  74. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-python/tests/test_conformance.py +0 -0
  75. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-python/tests/test_generated_models.py +0 -0
  76. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-python/tests/test_json.py +0 -0
  77. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-python/tests/test_mixed_content.py +0 -0
  78. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-python/tests/test_polyxml.py +0 -0
  79. {polyxml-0.34.3 → polyxml-0.34.4}/crates/polyxml-python/tests/test_xsi_type.py +0 -0
  80. {polyxml-0.34.3 → polyxml-0.34.4}/python/polyxml/AGENT_GUIDE.md +0 -0
  81. {polyxml-0.34.3 → polyxml-0.34.4}/python/polyxml/__init__.py +0 -0
  82. {polyxml-0.34.3 → polyxml-0.34.4}/python/polyxml/compat/__init__.py +0 -0
  83. {polyxml-0.34.3 → polyxml-0.34.4}/python/polyxml/compat/xsdata.py +0 -0
  84. {polyxml-0.34.3 → polyxml-0.34.4}/python/polyxml/py.typed +0 -0
@@ -608,7 +608,7 @@ dependencies = [
608
608
 
609
609
  [[package]]
610
610
  name = "polyxml"
611
- version = "0.34.3"
611
+ version = "0.34.4"
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.34.3"
632
+ version = "0.34.4"
633
633
  dependencies = [
634
634
  "polyxml",
635
635
  ]
636
636
 
637
637
  [[package]]
638
638
  name = "polyxml-cli"
639
- version = "0.34.3"
639
+ version = "0.34.4"
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.34.3"
653
+ version = "0.34.4"
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.34.3"
663
+ version = "0.34.4"
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.34.3"
673
+ version = "0.34.4"
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.34.3"
6
+ version = "0.34.4"
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.34.3
3
+ Version: 0.34.4
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -1426,7 +1426,10 @@ impl CSharpCodegen {
1426
1426
  format!(" : {}", base_clause.join(", "))
1427
1427
  };
1428
1428
 
1429
- let mut seen_props = HashSet::new();
1429
+ // Reserve the actual generated names on every ancestor. Distinct
1430
+ // XML members (e.g. nameOfClass attribute and NameOfClass element)
1431
+ // can normalize to the same C# identifier.
1432
+ let mut seen_props = self.inherited_property_names(s, ir, &mut HashSet::new());
1430
1433
  let prop_names: Vec<String> = s
1431
1434
  .fields
1432
1435
  .iter()
@@ -1678,6 +1681,51 @@ impl CSharpCodegen {
1678
1681
  writeln!(out, "{}}}\n", indent).unwrap();
1679
1682
  }
1680
1683
 
1684
+ fn inherited_property_names(
1685
+ &self,
1686
+ s: &StructDef,
1687
+ ir: &SchemaIR,
1688
+ visited: &mut HashSet<QName>,
1689
+ ) -> HashSet<String> {
1690
+ let Some(base) = s.base_type.as_ref() else {
1691
+ return HashSet::new();
1692
+ };
1693
+ if !visited.insert(base.clone()) {
1694
+ return HashSet::new();
1695
+ }
1696
+ let Some(TypeDef::Struct(parent)) = ir.types.get(base) else {
1697
+ return HashSet::new();
1698
+ };
1699
+ let mut names = self.inherited_property_names(parent, ir, visited);
1700
+ let mut ancestor_fields = Vec::new();
1701
+ let mut next = parent.base_type.as_ref();
1702
+ let mut seen = HashSet::new();
1703
+ while let Some(qname) = next {
1704
+ if !seen.insert(qname.clone()) {
1705
+ break;
1706
+ }
1707
+ let Some(TypeDef::Struct(ancestor)) = ir.types.get(qname) else {
1708
+ break;
1709
+ };
1710
+ ancestor_fields.extend(ancestor.fields.iter());
1711
+ next = ancestor.base_type.as_ref();
1712
+ }
1713
+ let enclosing = type_ident(&parent.qname);
1714
+ for field in &parent.fields {
1715
+ let inherited = ancestor_fields.iter().any(|f| {
1716
+ (field.kind == FieldKind::Text && f.kind == FieldKind::Text)
1717
+ || (field.kind == f.kind
1718
+ && field.xml_name == f.xml_name
1719
+ && field.namespace == f.namespace
1720
+ && field.name == f.name)
1721
+ });
1722
+ if !inherited {
1723
+ self.unique_property_name(&field.name, &mut names, Some(&enclosing));
1724
+ }
1725
+ }
1726
+ names
1727
+ }
1728
+
1681
1729
  fn unique_property_name(
1682
1730
  &self,
1683
1731
  name: &str,
@@ -1333,7 +1333,10 @@ impl RustCodegen {
1333
1333
  if field.kind != FieldKind::Element {
1334
1334
  continue;
1335
1335
  }
1336
- let tags: Vec<String> = match self.resolve_union_def(&field.type_ref, ir) {
1336
+ let tags: Vec<String> = match self
1337
+ .resolve_union_def(&field.type_ref, ir)
1338
+ .filter(|_| field.xml_name.is_empty())
1339
+ {
1337
1340
  Some(u) => u
1338
1341
  .branches
1339
1342
  .iter()
@@ -1495,7 +1498,10 @@ impl RustCodegen {
1495
1498
  }
1496
1499
 
1497
1500
  for meta in &elem_fields {
1498
- if let Some(union_def) = self.resolve_union_def(&meta.field.type_ref, ir) {
1501
+ if let Some(union_def) = self
1502
+ .resolve_union_def(&meta.field.type_ref, ir)
1503
+ .filter(|_| meta.field.xml_name.is_empty())
1504
+ {
1499
1505
  let branch_tags: Vec<_> = union_def
1500
1506
  .branches
1501
1507
  .iter()
@@ -1575,7 +1581,10 @@ impl RustCodegen {
1575
1581
  );
1576
1582
  }
1577
1583
  for meta in &elem_fields {
1578
- if let Some(union_def) = self.resolve_union_def(&meta.field.type_ref, ir) {
1584
+ if let Some(union_def) = self
1585
+ .resolve_union_def(&meta.field.type_ref, ir)
1586
+ .filter(|_| meta.field.xml_name.is_empty())
1587
+ {
1579
1588
  let branch_tags: Vec<_> = union_def
1580
1589
  .branches
1581
1590
  .iter()
@@ -2069,10 +2078,14 @@ impl RustCodegen {
2069
2078
  } else {
2070
2079
  let target_type = self.format_rust_type_ref(&field.type_ref, types_with_lifetime);
2071
2080
  let target_clean = target_type.split('<').next().unwrap_or(&target_type);
2081
+ let decode = if self.resolve_union_def(&field.type_ref, ir).is_some() {
2082
+ "decode_xml_wrapped"
2083
+ } else {
2084
+ "decode_xml"
2085
+ };
2072
2086
  let _ = writeln!(
2073
2087
  out,
2074
- " let val = {}::decode_xml(reader, &e)?;",
2075
- target_clean
2088
+ " let val = {target_clean}::{decode}(reader, &e)?;"
2076
2089
  );
2077
2090
  let final_val = if is_boxed { "Box::new(val)" } else { "val" };
2078
2091
  if is_list {
@@ -2102,6 +2115,10 @@ impl RustCodegen {
2102
2115
  let is_list = field.cardinality.is_list() || field.type_ref.is_list();
2103
2116
  let is_boxed = field.is_cycle_cut || field.type_ref.is_boxed();
2104
2117
 
2118
+ if self.resolve_union_def(&field.type_ref, ir).is_some() {
2119
+ let _ = writeln!(out, " return Err(PolyXmlError::SchemaError(\"Missing choice in {}\".into()));", field.xml_name);
2120
+ return;
2121
+ }
2105
2122
  if self.field_is_string(&field.type_ref, ir) {
2106
2123
  self.emit_pattern_check(out, field, "\"\"", ir);
2107
2124
  if self.options.zero_copy {
@@ -2399,7 +2416,15 @@ impl RustCodegen {
2399
2416
  field.xml_name
2400
2417
  );
2401
2418
  } else if self.resolve_union_def(&field.type_ref, ir).is_some() {
2402
- let _ = writeln!(out, " item.encode_xml(writer, None)?;",);
2419
+ if field.xml_name.is_empty() {
2420
+ let _ = writeln!(out, " item.encode_xml(writer, None)?;");
2421
+ } else {
2422
+ let _ = writeln!(
2423
+ out,
2424
+ " item.encode_xml_wrapped(writer, {:?})?;",
2425
+ field.xml_name
2426
+ );
2427
+ }
2403
2428
  } else {
2404
2429
  let _ = writeln!(
2405
2430
  out,
@@ -2451,7 +2476,15 @@ impl RustCodegen {
2451
2476
  field.xml_name
2452
2477
  );
2453
2478
  } else if self.resolve_union_def(&field.type_ref, ir).is_some() {
2454
- let _ = writeln!(out, " val.encode_xml(writer, None)?;",);
2479
+ if field.xml_name.is_empty() {
2480
+ let _ = writeln!(out, " val.encode_xml(writer, None)?;");
2481
+ } else {
2482
+ let _ = writeln!(
2483
+ out,
2484
+ " val.encode_xml_wrapped(writer, {:?})?;",
2485
+ field.xml_name
2486
+ );
2487
+ }
2455
2488
  } else {
2456
2489
  let _ = writeln!(
2457
2490
  out,
@@ -2506,7 +2539,15 @@ impl RustCodegen {
2506
2539
  field.xml_name
2507
2540
  );
2508
2541
  } else if self.resolve_union_def(&field.type_ref, ir).is_some() {
2509
- let _ = writeln!(out, " self.{}.encode_xml(writer, None)?;", rust_name);
2542
+ if field.xml_name.is_empty() {
2543
+ let _ = writeln!(out, " self.{}.encode_xml(writer, None)?;", rust_name);
2544
+ } else {
2545
+ let _ = writeln!(
2546
+ out,
2547
+ " self.{}.encode_xml_wrapped(writer, {:?})?;",
2548
+ rust_name, field.xml_name
2549
+ );
2550
+ }
2510
2551
  } else {
2511
2552
  let _ = writeln!(
2512
2553
  out,
@@ -2540,6 +2581,35 @@ impl RustCodegen {
2540
2581
  out.push('\n');
2541
2582
  let _ = writeln!(out, "{} {{", impl_header);
2542
2583
 
2584
+ let reader_lifetime = if needs_lifetime { "'a" } else { "'_" };
2585
+ let _ = writeln!(out, " pub fn decode_xml_wrapped(reader: &mut Reader<&{reader_lifetime} [u8]>, start: &BytesStart<'_>) -> Result<Self> {{");
2586
+ out.push_str(r#" let mut value = None;
2587
+ loop {
2588
+ match reader.read_event()? {
2589
+ Event::Start(e) => {
2590
+ if value.is_some() { return Err(PolyXmlError::SchemaError("Multiple choice alternatives".into())); }
2591
+ value = Some(Self::decode_xml(reader, &e)?);
2592
+ }
2593
+ Event::Empty(e) => {
2594
+ if value.is_some() { return Err(PolyXmlError::SchemaError("Multiple choice alternatives".into())); }
2595
+ let mut empty_reader = Reader::from_str("");
2596
+ value = Some(Self::decode_xml(&mut empty_reader, &e)?);
2597
+ }
2598
+ Event::End(e) if e.name() == start.name() => break,
2599
+ Event::Eof => return Err(PolyXmlError::SchemaError("Unclosed choice wrapper".into())),
2600
+ _ => {}
2601
+ }
2602
+ }
2603
+ value.ok_or_else(|| PolyXmlError::SchemaError("Missing choice alternative".into()))
2604
+ }
2605
+ pub fn encode_xml_wrapped<W: std::io::Write>(&self, writer: &mut Writer<W>, tag: &str) -> Result<()> {
2606
+ writer.write_event(Event::Start(BytesStart::new(tag)))?;
2607
+ self.encode_xml(writer, None)?;
2608
+ writer.write_event(Event::End(BytesEnd::new(tag)))?;
2609
+ Ok(())
2610
+ }
2611
+ "#);
2612
+
2543
2613
  let from_xml_sig = if needs_lifetime {
2544
2614
  "pub fn from_xml(xml: &'a str) -> Result<Self>"
2545
2615
  } else {
@@ -125,3 +125,46 @@ if(json.Contains("TypeXml"))throw new Exception("XML proxy leaked to JSON: "+jso
125
125
  );
126
126
  }
127
127
  }
128
+
129
+ #[test]
130
+ fn inherited_attribute_and_distinct_element_keep_separate_property_names() {
131
+ let ir = XsdParser::new().parse_str(r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
132
+ <xs:complexType name="Entity"><xs:attribute name="nameOfClass" type="xs:string"/></xs:complexType>
133
+ <xs:complexType name="Assignment"><xs:complexContent><xs:extension base="Entity"><xs:sequence><xs:element name="NameOfClass" type="xs:string" minOccurs="0"/></xs:sequence></xs:extension></xs:complexContent></xs:complexType>
134
+ <xs:complexType name="SpecialAssignment"><xs:complexContent><xs:extension base="Assignment"><xs:sequence><xs:element name="name_of_class" type="xs:string" minOccurs="0"/></xs:sequence></xs:extension></xs:complexContent></xs:complexType>
135
+ </xs:schema>"#).unwrap();
136
+ for use_records in [true, false] {
137
+ let temp = tempdir().unwrap();
138
+ fs::write(temp.path().join("App.csproj"), r#"<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><OutputType>Exe</OutputType><TargetFramework>net8.0</TargetFramework><ImplicitUsings>enable</ImplicitUsings><Nullable>enable</Nullable><WarningsAsErrors>CS0108;CS8866;CS0657;CS8907</WarningsAsErrors></PropertyGroup></Project>"#).unwrap();
139
+ fs::write(
140
+ temp.path().join("Models.cs"),
141
+ CSharpCodegen::new(CSharpOptions {
142
+ namespace: "Models".into(),
143
+ use_records,
144
+ ..Default::default()
145
+ })
146
+ .generate_module(&ir),
147
+ )
148
+ .unwrap();
149
+ fs::write(temp.path().join("Program.cs"), r#"using System.Xml.Serialization;using System.Xml.Linq;using Models;
150
+ var serializer=new XmlSerializer(typeof(SpecialAssignment));
151
+ var value=(SpecialAssignment)serializer.Deserialize(new StringReader("<SpecialAssignment nameOfClass='attribute'><NameOfClass>element</NameOfClass><name_of_class>grandchild</name_of_class></SpecialAssignment>"))!;
152
+ if(((Entity)value).NameOfClass!="attribute"||value.NameOfClass2!="element"||value.NameOfClass3!="grandchild")throw new Exception("inherited values lost");
153
+ var writer=new StringWriter();serializer.Serialize(writer,value);var xml=XDocument.Parse(writer.ToString()).Root!;
154
+ if((string?)xml.Attribute("nameOfClass")!="attribute"||(string?)xml.Element("NameOfClass")!="element"||(string?)xml.Element("name_of_class")!="grandchild")throw new Exception(writer.ToString());
155
+ var json=System.Text.Json.JsonSerializer.Serialize(value);if(!json.Contains("attribute")||!json.Contains("element")||!json.Contains("grandchild"))throw new Exception(json);
156
+ "#).unwrap();
157
+ let result = Command::new("dotnet")
158
+ .args(["run", "--disable-build-servers"])
159
+ .env("DOTNET_CLI_USE_MSBUILD_SERVER", "0")
160
+ .current_dir(temp.path())
161
+ .output()
162
+ .unwrap();
163
+ assert!(
164
+ result.status.success(),
165
+ "records={use_records}: {}{}",
166
+ String::from_utf8_lossy(&result.stdout),
167
+ String::from_utf8_lossy(&result.stderr)
168
+ );
169
+ }
170
+ }
@@ -1087,7 +1087,7 @@ fn test_rust_phf_dispatch_union_groups_branch_tags() {
1087
1087
  is_mixed: false,
1088
1088
  fields: vec![FieldDef::new(
1089
1089
  "method",
1090
- "method",
1090
+ "",
1091
1091
  FieldKind::Element,
1092
1092
  TypeRef::Named(QName::local("PaymentMethod")),
1093
1093
  )],
@@ -1364,3 +1364,62 @@ fn main() {
1364
1364
  );
1365
1365
  }
1366
1366
  }
1367
+
1368
+ #[test]
1369
+ fn named_choice_wrappers_round_trip_without_unreachable_dispatch() {
1370
+ let schema = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
1371
+ <xs:complexType name="Position"><xs:choice><xs:element name="Percentage" type="xs:double"/><xs:element name="NumberOfSteps" type="xs:unsignedInt"/><xs:element name="Label" type="xs:string"/></xs:choice></xs:complexType>
1372
+ <xs:complexType name="Sweep"><xs:sequence><xs:element name="StartingPosition" type="Position"/><xs:element name="StepIncrement" type="Position"/><xs:element name="Optional" type="Position" minOccurs="0"/><xs:element name="Repeated" type="Position" minOccurs="0" maxOccurs="unbounded"/></xs:sequence></xs:complexType>
1373
+ </xs:schema>"#;
1374
+ let ir = XsdParser::new().parse_str(schema).unwrap();
1375
+ let core = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
1376
+ for (zero_copy, phf) in [(true, false), (false, false), (true, true)] {
1377
+ let dir = tempfile::tempdir().unwrap();
1378
+ std::fs::create_dir(dir.path().join("src")).unwrap();
1379
+ std::fs::write(dir.path().join("Cargo.toml"), format!("[package]\nname=\"binary-lexical-test\"\nversion=\"0.0.0\"\nedition=\"2021\"\n[dependencies]\npolyxml={{path={core:?}}}\nquick-xml={{version=\"0.42\",features=[\"serialize\"]}}\nserde={{version=\"1\",features=[\"derive\"]}}\nserde_json=\"1\"\nregex=\"1\"\nphf={{version=\"0.14.0\",features=[\"macros\"]}}\n")).unwrap();
1380
+ let code = RustCodegen::new(RustOptions {
1381
+ zero_copy,
1382
+ phf,
1383
+ ..Default::default()
1384
+ })
1385
+ .generate_module(&ir);
1386
+ std::fs::write(dir.path().join("src/models.rs"), code).unwrap();
1387
+ std::fs::write(dir.path().join("src/main.rs"), r#"mod models;
1388
+ use models::*;
1389
+ fn main() {
1390
+ let xml="<Sweep><StartingPosition><Percentage>25</Percentage></StartingPosition><StepIncrement><NumberOfSteps>3</NumberOfSteps></StepIncrement><Optional><Label>focus</Label></Optional><Repeated><Percentage>2</Percentage></Repeated><Repeated><NumberOfSteps>4</NumberOfSteps></Repeated></Sweep>";
1391
+ let value=Sweep::from_xml(xml).unwrap();
1392
+ assert!(matches!(value.starting_position, Position::Percentage(25.0)));
1393
+ assert!(matches!(value.step_increment, Position::NumberOfSteps(3)));
1394
+ assert_eq!(value.repeated.len(),2);
1395
+ assert_eq!(value.to_xml_string().unwrap(),xml);
1396
+ let xml="<Sweep><StartingPosition><Label/></StartingPosition><StepIncrement><NumberOfSteps>3</NumberOfSteps></StepIncrement></Sweep>";
1397
+ let value=Sweep::from_xml(xml).unwrap();
1398
+ assert!(matches!(&value.starting_position,Position::Label(text) if text.is_empty()));
1399
+ assert!(value.optional.is_none());
1400
+ let round_trip=value.to_xml_string().unwrap(); assert!(round_trip.contains("<StartingPosition><Label></Label></StartingPosition>"));
1401
+ for inner in ["", "<Percentage>1</Percentage><NumberOfSteps>2</NumberOfSteps>", "<Unknown/>"] {
1402
+ let xml=format!("<Sweep><StartingPosition>{inner}</StartingPosition><StepIncrement><NumberOfSteps>3</NumberOfSteps></StepIncrement></Sweep>");
1403
+ assert!(Sweep::from_xml(&xml).is_err(),"accepted {xml}");
1404
+ }
1405
+ assert!(Sweep::from_xml("<Sweep><StartingPosition/><StepIncrement><NumberOfSteps>3</NumberOfSteps></StepIncrement></Sweep>").is_err());
1406
+ }
1407
+ "#).unwrap();
1408
+ let output = std::process::Command::new("cargo")
1409
+ .args(["run", "--quiet", "--offline"])
1410
+ .env("RUSTFLAGS", "-D unreachable-patterns")
1411
+ .env(
1412
+ "CARGO_TARGET_DIR",
1413
+ core.join("../../target/binary-lexical-tests"),
1414
+ )
1415
+ .current_dir(dir.path())
1416
+ .output()
1417
+ .unwrap();
1418
+ assert!(
1419
+ output.status.success(),
1420
+ "zero_copy={zero_copy}, phf={phf}: {}\n{}",
1421
+ String::from_utf8_lossy(&output.stdout),
1422
+ String::from_utf8_lossy(&output.stderr)
1423
+ );
1424
+ }
1425
+ }
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "polyxml"
7
- version = "0.34.3"
7
+ version = "0.34.4"
8
8
  description = "High-performance, polyglot XML data-binding engine built in Rust"
9
9
  authors = [
10
10
  { name = "Bailey Nguyen", email = "bailey.tan.nguyen@gmail.com" }
File without changes