polyxml 0.34.2__tar.gz → 0.34.3__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.2 → polyxml-0.34.3}/Cargo.lock +6 -6
  2. {polyxml-0.34.2 → polyxml-0.34.3}/Cargo.toml +1 -1
  3. {polyxml-0.34.2 → polyxml-0.34.3}/PKG-INFO +1 -1
  4. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/codegen/csharp/mod.rs +51 -0
  5. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/schema_parser/mod.rs +88 -25
  6. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_qualified_attributes.rs +50 -3
  7. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_schema_ir.rs +137 -0
  8. {polyxml-0.34.2 → polyxml-0.34.3}/pyproject.toml +1 -1
  9. {polyxml-0.34.2 → polyxml-0.34.3}/README.md +0 -0
  10. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/Cargo.toml +0 -0
  11. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/README.md +0 -0
  12. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/benches/core_benchmarks.rs +0 -0
  13. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/benches/support/tag_dispatch_fixtures.rs +0 -0
  14. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/benches/tag_dispatch.rs +0 -0
  15. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/codegen/cpp/mod.rs +0 -0
  16. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/codegen/go/mod.rs +0 -0
  17. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/codegen/java/codec.rs +0 -0
  18. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/codegen/java/mod.rs +0 -0
  19. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/codegen/java/models.rs +0 -0
  20. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/codegen/mod.rs +0 -0
  21. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/codegen/python/aot.rs +0 -0
  22. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/codegen/python/mod.rs +0 -0
  23. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/codegen/rust/mod.rs +0 -0
  24. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/codegen/typescript/mod.rs +0 -0
  25. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/converters.rs +0 -0
  26. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/error.rs +0 -0
  27. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/integer.rs +0 -0
  28. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/ir/chunker.rs +0 -0
  29. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/ir/mod.rs +0 -0
  30. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/ir/particle.rs +0 -0
  31. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/ir/tarjan.rs +0 -0
  32. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/json.rs +0 -0
  33. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/lib.rs +0 -0
  34. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/parser.rs +0 -0
  35. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/schema.rs +0 -0
  36. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/schema_parser/upa.rs +0 -0
  37. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/serializer.rs +0 -0
  38. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/transcoder.rs +0 -0
  39. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/src/value.rs +0 -0
  40. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/fixtures/lexical_union.xsd +0 -0
  41. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/fixtures/mixed_content.xsd +0 -0
  42. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/fixtures/ts_mixed_content_inheritance.xsd +0 -0
  43. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_adversarial_identifiers.rs +0 -0
  44. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_any_attribute.rs +0 -0
  45. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_any_element.rs +0 -0
  46. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_conformance.rs +0 -0
  47. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_core.rs +0 -0
  48. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_cpp_codegen.rs +0 -0
  49. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_csharp_codegen.rs +0 -0
  50. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_go_codegen.rs +0 -0
  51. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_java_codegen.rs +0 -0
  52. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_json.rs +0 -0
  53. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_lexical_lists.rs +0 -0
  54. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_lexical_union_codegen.rs +0 -0
  55. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_mixed_content.rs +0 -0
  56. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_pattern_codegen.rs +0 -0
  57. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_python_codegen.rs +0 -0
  58. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_rust_codecs.rs +0 -0
  59. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_rust_codegen.rs +0 -0
  60. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_schema_audit.rs +0 -0
  61. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_security.rs +0 -0
  62. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_temporal_union.rs +0 -0
  63. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_transcoder.rs +0 -0
  64. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_ts_codegen.rs +0 -0
  65. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_ubl_csharp.rs +0 -0
  66. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_unbounded_choice.rs +0 -0
  67. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_upa.rs +0 -0
  68. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_value_constraints.rs +0 -0
  69. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-core/tests/test_xsi_type.rs +0 -0
  70. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-python/Cargo.toml +0 -0
  71. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-python/README.md +0 -0
  72. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-python/benches/aot_vs_dataclass.py +0 -0
  73. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-python/src/lib.rs +0 -0
  74. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-python/tests/test_conformance.py +0 -0
  75. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-python/tests/test_generated_models.py +0 -0
  76. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-python/tests/test_json.py +0 -0
  77. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-python/tests/test_mixed_content.py +0 -0
  78. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-python/tests/test_polyxml.py +0 -0
  79. {polyxml-0.34.2 → polyxml-0.34.3}/crates/polyxml-python/tests/test_xsi_type.py +0 -0
  80. {polyxml-0.34.2 → polyxml-0.34.3}/python/polyxml/AGENT_GUIDE.md +0 -0
  81. {polyxml-0.34.2 → polyxml-0.34.3}/python/polyxml/__init__.py +0 -0
  82. {polyxml-0.34.2 → polyxml-0.34.3}/python/polyxml/compat/__init__.py +0 -0
  83. {polyxml-0.34.2 → polyxml-0.34.3}/python/polyxml/compat/xsdata.py +0 -0
  84. {polyxml-0.34.2 → polyxml-0.34.3}/python/polyxml/py.typed +0 -0
@@ -608,7 +608,7 @@ dependencies = [
608
608
 
609
609
  [[package]]
610
610
  name = "polyxml"
611
- version = "0.34.2"
611
+ version = "0.34.3"
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.2"
632
+ version = "0.34.3"
633
633
  dependencies = [
634
634
  "polyxml",
635
635
  ]
636
636
 
637
637
  [[package]]
638
638
  name = "polyxml-cli"
639
- version = "0.34.2"
639
+ version = "0.34.3"
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.2"
653
+ version = "0.34.3"
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.2"
663
+ version = "0.34.3"
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.2"
673
+ version = "0.34.3"
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.2"
6
+ version = "0.34.3"
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.2
3
+ Version: 0.34.3
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -1099,6 +1099,53 @@ impl CSharpCodegen {
1099
1099
  true
1100
1100
  }
1101
1101
 
1102
+ fn emit_optional_enum_attribute_proxy(
1103
+ &self,
1104
+ out: &mut String,
1105
+ field: &FieldDef,
1106
+ name: &str,
1107
+ ir: &SchemaIR,
1108
+ indent: &str,
1109
+ ) -> bool {
1110
+ if !self.options.emit_xml_attributes
1111
+ || field.kind != FieldKind::Attribute
1112
+ || !field.cardinality.is_optional()
1113
+ || field.cardinality.is_list()
1114
+ {
1115
+ return false;
1116
+ }
1117
+ let TypeRef::Named(qname) = &field.type_ref else {
1118
+ return false;
1119
+ };
1120
+ if !matches!(ir.types.get(qname), Some(TypeDef::Enum(_))) {
1121
+ return false;
1122
+ }
1123
+ let ty = self.map_field_type(field, ir);
1124
+ let base = ty.trim_end_matches('?');
1125
+ let namespace = field
1126
+ .namespace
1127
+ .as_ref()
1128
+ .map(|ns| format!(", Namespace = {ns:?}"))
1129
+ .unwrap_or_default();
1130
+ if self.options.emit_json_attributes {
1131
+ writeln!(out, "{indent} [JsonPropertyName({:?})]", field.xml_name).unwrap();
1132
+ }
1133
+ writeln!(
1134
+ out,
1135
+ "{indent} [XmlIgnore] public {ty} {name} {{ get; set; }}"
1136
+ )
1137
+ .unwrap();
1138
+ if self.options.emit_json_attributes {
1139
+ // Keep the XML lexical proxy out of JSON, preserving the typed property.
1140
+ writeln!(out, "{indent} [JsonIgnore]").unwrap();
1141
+ }
1142
+ writeln!(out, "{indent} [XmlAttribute({:?}{namespace})] public string? {name}Xml {{
1143
+ {indent} get => {name}?.ToXmlValue();
1144
+ {indent} set => {name} = value is null ? null : ({base})new XmlSerializer(typeof({base}), new XmlRootAttribute(\"value\")).Deserialize(new System.IO.StringReader(\"<value>\" + System.Security.SecurityElement.Escape(value) + \"</value>\"))!;
1145
+ {indent} }}", field.xml_name).unwrap();
1146
+ true
1147
+ }
1148
+
1102
1149
  fn emit_default_scalar_proxy(
1103
1150
  &self,
1104
1151
  out: &mut String,
@@ -1392,6 +1439,8 @@ impl CSharpCodegen {
1392
1439
  }
1393
1440
 
1394
1441
  let has_lexical_union = self.options.emit_xml_attributes && s.fields.iter().any(|f| {
1442
+ (f.kind == FieldKind::Attribute && f.cardinality.is_optional()
1443
+ && matches!(&f.type_ref, TypeRef::Named(q) if matches!(ir.types.get(q), Some(TypeDef::Enum(_))))) ||
1395
1444
  matches!(f.type_ref, TypeRef::Primitive(p) if p.is_unbounded_integer()) || matches!(f.type_ref, TypeRef::Primitive(PrimitiveType::Date | PrimitiveType::DateTime | PrimitiveType::Time | PrimitiveType::Duration)) || f.default_value.is_some() || f.fixed_value.is_some() || matches!(&f.type_ref, TypeRef::Named(q) if matches!(ir.types.get(q), Some(TypeDef::Union(u)) if u.is_lexical()))
1396
1445
  });
1397
1446
 
@@ -1404,6 +1453,7 @@ impl CSharpCodegen {
1404
1453
  .unwrap();
1405
1454
  for (f, name) in s.fields.iter().zip(&prop_names) {
1406
1455
  if self.emit_default_scalar_proxy(out, f, name, ir, indent)
1456
+ || self.emit_optional_enum_attribute_proxy(out, f, name, ir, indent)
1407
1457
  || self.emit_integer_proxy(out, f, name, ir, indent)
1408
1458
  || self.emit_temporal_proxy(out, f, name, ir, indent)
1409
1459
  {
@@ -1471,6 +1521,7 @@ impl CSharpCodegen {
1471
1521
  writeln!(out, "{} public {}() {{ }}", indent, struct_name).unwrap();
1472
1522
  for (f, name) in s.fields.iter().zip(&prop_names) {
1473
1523
  if self.emit_default_scalar_proxy(out, f, name, ir, indent)
1524
+ || self.emit_optional_enum_attribute_proxy(out, f, name, ir, indent)
1474
1525
  || self.emit_integer_proxy(out, f, name, ir, indent)
1475
1526
  || self.emit_temporal_proxy(out, f, name, ir, indent)
1476
1527
  {
@@ -350,20 +350,13 @@ impl XsdParser {
350
350
  }
351
351
  }
352
352
  "attribute" => {
353
- if let (Some(name), Some(ty)) =
354
- (get_attr_value(e, "name"), get_attr_value(e, "type"))
355
- {
356
- ir.attributes
357
- .entry(QName::new(target_namespace.as_deref(), name))
358
- .or_insert_with(|| {
359
- resolve_type_ref(
360
- &ty,
361
- target_namespace.as_deref(),
362
- &prefixes,
363
- )
364
- });
365
- }
366
- skip_subtree(&mut reader)?;
353
+ self.parse_global_attribute(
354
+ &mut reader,
355
+ e,
356
+ target_namespace.as_deref(),
357
+ &prefixes,
358
+ &mut ir,
359
+ )?;
367
360
  }
368
361
  "group" => {
369
362
  // Named model group definition.
@@ -445,18 +438,19 @@ impl XsdParser {
445
438
  }
446
439
  }
447
440
  "attribute" => {
448
- if let (Some(name), Some(ty)) =
449
- (get_attr_value(e, "name"), get_attr_value(e, "type"))
450
- {
451
- ir.attributes
452
- .entry(QName::new(target_namespace.as_deref(), name))
453
- .or_insert_with(|| {
441
+ if let Some(name) = get_attr_value(e, "name") {
442
+ let type_ref = get_attr_value(e, "type")
443
+ .map(|ty| {
454
444
  resolve_type_ref(
455
445
  &ty,
456
446
  target_namespace.as_deref(),
457
447
  &prefixes,
458
448
  )
459
- });
449
+ })
450
+ .unwrap_or_else(TypeRef::string);
451
+ ir.attributes
452
+ .entry(QName::new(target_namespace.as_deref(), name))
453
+ .or_insert(type_ref);
460
454
  }
461
455
  }
462
456
  "group" => {
@@ -1225,6 +1219,7 @@ impl XsdParser {
1225
1219
  let mut facets = RestrictionFacets::default();
1226
1220
  let mut enum_values = Vec::new();
1227
1221
  let mut union_branches: Option<Vec<UnionBranch>> = None;
1222
+ let mut list_requires_inline = false;
1228
1223
  let mut documentation = None;
1229
1224
  let mut buf = Vec::new();
1230
1225
 
@@ -1252,14 +1247,30 @@ impl XsdParser {
1252
1247
  &item, target_ns, prefixes,
1253
1248
  )));
1254
1249
  } else {
1255
- return Err(SchemaError::Malformed(
1256
- "xs:list requires a supported itemType".into(),
1257
- ));
1250
+ list_requires_inline = true;
1258
1251
  }
1259
1252
  }
1260
1253
  "union" => {
1261
1254
  union_branches = Some(parse_union_members(e, target_ns, prefixes));
1262
1255
  }
1256
+ "simpleType" if list_requires_inline => {
1257
+ let item_name =
1258
+ unique_type_name(ir, target_ns, &format!("{}Item", qname.local));
1259
+ if let Some(item) = self.parse_simple_type(
1260
+ reader,
1261
+ e,
1262
+ target_ns,
1263
+ prefixes,
1264
+ Some(item_name),
1265
+ ir,
1266
+ )? {
1267
+ base_type =
1268
+ TypeRef::List(Box::new(TypeRef::Named(item.qname().clone())));
1269
+ ir.add_type(item);
1270
+ list_requires_inline = false;
1271
+ }
1272
+ depth -= 1;
1273
+ }
1263
1274
  "simpleType" if union_branches.is_some() => {
1264
1275
  let branch_index = union_branches.as_ref().unwrap().len() + 1;
1265
1276
  let inline_name = unique_type_name(
@@ -1380,7 +1391,12 @@ impl XsdParser {
1380
1391
  _ => {}
1381
1392
  }
1382
1393
  }
1383
- Event::End(_) => {
1394
+ Event::End(ref e) => {
1395
+ if strip_prefix(e.name().into_inner()) == "list" && list_requires_inline {
1396
+ return Err(SchemaError::Malformed(
1397
+ "xs:list requires itemType or an inline simpleType".into(),
1398
+ ));
1399
+ }
1384
1400
  depth -= 1;
1385
1401
  }
1386
1402
  Event::Eof => break,
@@ -1465,6 +1481,46 @@ impl XsdParser {
1465
1481
  }
1466
1482
  }
1467
1483
 
1484
+ fn parse_global_attribute(
1485
+ &self,
1486
+ reader: &mut Reader<&[u8]>,
1487
+ start: &BytesStart,
1488
+ target_ns: Option<&str>,
1489
+ prefixes: &HashMap<String, String>,
1490
+ ir: &mut SchemaIR,
1491
+ ) -> Result<(), SchemaError> {
1492
+ let Some(name) = get_attr_value(start, "name") else {
1493
+ return skip_subtree(reader);
1494
+ };
1495
+ let mut type_ref = get_attr_value(start, "type")
1496
+ .map(|ty| resolve_type_ref(&ty, target_ns, prefixes))
1497
+ .unwrap_or_else(TypeRef::string);
1498
+ loop {
1499
+ match reader.read_event()? {
1500
+ Event::Start(e) if strip_prefix(e.name().into_inner()) == "simpleType" => {
1501
+ let anonymous_name =
1502
+ unique_type_name(ir, target_ns, &format!("{name}AttributeType"));
1503
+ if let Some(definition) = self.parse_simple_type(
1504
+ reader,
1505
+ &e,
1506
+ target_ns,
1507
+ prefixes,
1508
+ Some(anonymous_name),
1509
+ ir,
1510
+ )? {
1511
+ type_ref = TypeRef::Named(definition.qname().clone());
1512
+ ir.add_type(definition);
1513
+ }
1514
+ }
1515
+ Event::Start(_) => skip_subtree(reader)?,
1516
+ Event::End(_) | Event::Eof => break,
1517
+ _ => {}
1518
+ }
1519
+ }
1520
+ ir.attributes.insert(QName::new(target_ns, name), type_ref);
1521
+ Ok(())
1522
+ }
1523
+
1468
1524
  fn parse_global_element(
1469
1525
  &mut self,
1470
1526
  reader: &mut Reader<&[u8]>,
@@ -2728,6 +2784,7 @@ fn validate_type_references(ir: &SchemaIR, xml: &str) -> Result<(), SchemaError>
2728
2784
  }
2729
2785
  fn validate_schema_grammar(xml: &str) -> Result<(), SchemaError> {
2730
2786
  let mut reader = Reader::from_str(xml);
2787
+ let mut schema_namespace = None;
2731
2788
  let mut stack = Vec::<(String, HashMap<String, String>)>::new();
2732
2789
  let mut line = 1;
2733
2790
  let mut line_position = 0;
@@ -2754,6 +2811,9 @@ fn validate_schema_grammar(xml: &str) -> Result<(), SchemaError> {
2754
2811
  }
2755
2812
  }
2756
2813
  let local = strip_prefix(element.name().into_inner());
2814
+ if stack.is_empty() && local == "schema" {
2815
+ schema_namespace = get_attr_value(&element, "targetNamespace");
2816
+ }
2757
2817
  let position = reader.buffer_position() as usize;
2758
2818
  line += xml[line_position..position]
2759
2819
  .bytes()
@@ -2804,6 +2864,9 @@ fn validate_schema_grammar(xml: &str) -> Result<(), SchemaError> {
2804
2864
  )));
2805
2865
  };
2806
2866
  if namespace == "http://www.w3.org/2001/XMLSchema"
2867
+ // The schema-for-schemas declares named types in this
2868
+ // namespace; the resolved IR validates their existence.
2869
+ && schema_namespace.as_deref() != Some(namespace.as_str())
2807
2870
  && attr.key.as_ref() != "ref"
2808
2871
  && PrimitiveType::from_xsd_name(local).is_none()
2809
2872
  {
@@ -15,11 +15,35 @@ fn global_attribute_references_round_trip_by_namespace() {
15
15
  "/../../research/fixtures/wave6/xlink_shared_attribute/root.xsd"
16
16
  ))
17
17
  .unwrap();
18
+ assert_attribute_round_trip(&ir);
19
+ }
20
+
21
+ #[test]
22
+ fn imported_inline_global_attribute_enum_round_trips() {
23
+ let fixture = tempdir().unwrap();
24
+ fs::copy(
25
+ concat!(
26
+ env!("CARGO_MANIFEST_DIR"),
27
+ "/../../research/fixtures/wave6/xlink_shared_attribute/root.xsd"
28
+ ),
29
+ fixture.path().join("root.xsd"),
30
+ )
31
+ .unwrap();
32
+ fs::write(fixture.path().join("xlink.xsd"), r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.w3.org/1999/xlink">
33
+ <xs:attribute name="type"><xs:simpleType><xs:restriction base="xs:string"><xs:enumeration value="resource"/><xs:enumeration value="arc"/></xs:restriction></xs:simpleType></xs:attribute>
34
+ </xs:schema>"#).unwrap();
35
+ let ir = XsdParser::new()
36
+ .parse_file(fixture.path().join("root.xsd"))
37
+ .unwrap();
38
+ assert_attribute_round_trip(&ir);
39
+ }
40
+
41
+ fn assert_attribute_round_trip(ir: &polyxml::ir::SchemaIR) {
18
42
  let temp = tempdir().unwrap();
19
43
  fs::write(temp.path().join("go.mod"), "module attrs\n\ngo 1.22\n").unwrap();
20
44
  fs::write(
21
45
  temp.path().join("models.go"),
22
- GoCodegen::new(GoOptions::default()).generate_module(&ir),
46
+ GoCodegen::new(GoOptions::default()).generate_module(ir),
23
47
  )
24
48
  .unwrap();
25
49
  fs::write(temp.path().join("models_test.go"), r#"package models
@@ -32,6 +56,8 @@ func TestAttributes(t *testing.T) {
32
56
  for {token,err:=decoder.Token();if err==io.EOF{break};if err!=nil{t.Fatal(err)};if start,ok:=token.(xml.StartElement);ok{for _,attr:=range start.Attr{if attr.Name.Space=="http://www.w3.org/1999/xlink"&&attr.Name.Local=="type"{count++;if attr.Value!="resource"&&attr.Value!="arc"{t.Fatal(attr)}}}}}
33
57
  if count!=2{t.Fatalf("attributes lost: %s",data)}
34
58
  }
59
+ var absent Links; if err:=xml.Unmarshal([]byte(`<Links><Resource/><Arc/></Links>`),&absent);err!=nil{t.Fatal(err)}
60
+ data,err:=xml.Marshal(absent);if err!=nil{t.Fatal(err)};if strings.Contains(string(data),`type=`){t.Fatalf("absent attribute emitted: %s",data)}
35
61
  }"#).unwrap();
36
62
  let result = Command::new("go")
37
63
  .args(["test", "./..."])
@@ -54,7 +80,7 @@ func TestAttributes(t *testing.T) {
54
80
  use_records,
55
81
  ..Default::default()
56
82
  })
57
- .generate_module(&ir),
83
+ .generate_module(ir),
58
84
  )
59
85
  .unwrap();
60
86
  fs::write(temp.path().join("Program.cs"), r#"using System.Xml.Serialization;using System.Xml.Linq;using Models;
@@ -62,9 +88,30 @@ var serializer=new XmlSerializer(typeof(Links));
62
88
  foreach(var document in new[]{"<Links xmlns:xl='http://www.w3.org/1999/xlink'><Resource xl:type='resource'/><Arc xl:type='arc'/></Links>","<Links><Resource xmlns:b='http://www.w3.org/1999/xlink' b:type='resource'/><Arc xmlns:c='http://www.w3.org/1999/xlink' c:type='arc'/></Links>"}){
63
89
  var root=(Links)serializer.Deserialize(new StringReader(document))!;
64
90
  var writer=new StringWriter();serializer.Serialize(writer,root);
91
+ using(var jsonDocument=System.Text.Json.JsonSerializer.SerializeToDocument(root)){
92
+ if(!jsonDocument.RootElement.GetProperty("Resource").TryGetProperty("type",out _))throw new Exception("JSON wire name lost");
93
+ }
65
94
  var xml=XDocument.Parse(writer.ToString());XNamespace ns="http://www.w3.org/1999/xlink";
66
95
  if((string?)xml.Root!.Element("Resource")!.Attribute(ns+"type")!="resource"||(string?)xml.Root.Element("Arc")!.Attribute(ns+"type")!="arc")throw new Exception(writer.ToString());
67
- }"#).unwrap();
96
+ }
97
+ var absent=(Links)serializer.Deserialize(new StringReader("<Links><Resource/><Arc/></Links>"))!;
98
+ var absentWriter=new StringWriter();serializer.Serialize(absentWriter,absent);
99
+ if(XDocument.Parse(absentWriter.ToString()).Descendants().Attributes().Any(a=>a.Name.LocalName=="type"))throw new Exception(absentWriter.ToString());
100
+ var json=System.Text.Json.JsonSerializer.Serialize(absent);
101
+ if(json.Contains("TypeXml"))throw new Exception("XML proxy leaked to JSON: "+json);
102
+ "#).unwrap();
103
+ if ir
104
+ .types
105
+ .values()
106
+ .any(|definition| matches!(definition, polyxml::ir::TypeDef::Enum(_)))
107
+ {
108
+ use std::io::Write;
109
+ let mut program = fs::OpenOptions::new()
110
+ .append(true)
111
+ .open(temp.path().join("Program.cs"))
112
+ .unwrap();
113
+ writeln!(program, r#"bool rejected=false;try{{serializer.Deserialize(new StringReader("<Links xmlns:x='http://www.w3.org/1999/xlink'><Resource x:type='invalid'/><Arc/></Links>"));}}catch(InvalidOperationException){{rejected=true;}}if(!rejected)throw new Exception("invalid enum accepted");"#).unwrap();
114
+ }
68
115
  let result = Command::new("dotnet")
69
116
  .arg("run")
70
117
  .current_dir(temp.path())
@@ -652,3 +652,140 @@ fn schema_errors_preserve_forward_imports_and_annotation_payloads() {
652
652
  assert!(XsdParser::new().parse_str(schema).is_err());
653
653
  }
654
654
  }
655
+
656
+ #[test]
657
+ fn imported_inline_global_attribute_types_are_resolved() {
658
+ let dir = tempdir().unwrap();
659
+ std::fs::write(dir.path().join("attributes.xsd"), r#"<s:schema xmlns:s="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:attrs">
660
+ <s:attribute name="code"><s:annotation><s:documentation>Inline enum</s:documentation></s:annotation><s:simpleType><s:restriction base="s:string"><s:enumeration value="arc"/><s:enumeration value="resource"/></s:restriction></s:simpleType></s:attribute>
661
+ <s:attribute name="number"><s:simpleType><s:restriction base="s:int"><s:minInclusive value="3"/></s:restriction></s:simpleType></s:attribute>
662
+ <s:attribute name="lang"><s:simpleType><s:union memberTypes="s:language"><s:simpleType><s:restriction base="s:string"><s:enumeration value=""/></s:restriction></s:simpleType></s:union></s:simpleType></s:attribute>
663
+ <s:attribute name="plain"/>
664
+ <s:attribute name="annotated"><s:annotation><s:documentation>Untyped</s:documentation></s:annotation></s:attribute>
665
+ </s:schema>"#).unwrap();
666
+ let root = dir.path().join("root.xsd");
667
+ std::fs::write(&root, r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:a="urn:attrs">
668
+ <xs:import namespace="urn:attrs" schemaLocation="attributes.xsd"/>
669
+ <xs:complexType name="Record"><xs:attribute ref="a:code" use="required"/><xs:attribute ref="a:number"/><xs:attribute ref="a:lang"/><xs:attribute ref="a:plain"/><xs:attribute ref="a:annotated"/></xs:complexType>
670
+ </xs:schema>"#).unwrap();
671
+ let ir = XsdParser::new().parse_file(&root).unwrap();
672
+ let TypeDef::Struct(record) = &ir.types[&QName::new(None::<String>, "Record")] else {
673
+ panic!("Record");
674
+ };
675
+ for field in &record.fields {
676
+ assert_eq!(field.namespace.as_deref(), Some("urn:attrs"));
677
+ assert_eq!(
678
+ field.type_ref,
679
+ ir.attributes[&QName::new(Some("urn:attrs"), &field.xml_name)]
680
+ );
681
+ }
682
+ assert_eq!(record.fields[0].cardinality, Cardinality::required_one());
683
+ let TypeRef::Named(enum_name) = &record.fields[0].type_ref else {
684
+ panic!("enum type");
685
+ };
686
+ let TypeDef::Enum(enumeration) = &ir.types[enum_name] else {
687
+ panic!("enum declaration");
688
+ };
689
+ assert_eq!(
690
+ enumeration
691
+ .variants
692
+ .iter()
693
+ .map(|v| v.value.as_str())
694
+ .collect::<Vec<_>>(),
695
+ ["arc", "resource"]
696
+ );
697
+ let TypeRef::Named(number_name) = &record.fields[1].type_ref else {
698
+ panic!("number type");
699
+ };
700
+ let TypeDef::Simple(number) = &ir.types[number_name] else {
701
+ panic!("restriction");
702
+ };
703
+ assert_eq!(number.base_type, TypeRef::Primitive(PrimitiveType::Int));
704
+ assert_eq!(number.facets.min_inclusive.as_deref(), Some("3"));
705
+ let TypeRef::Named(union_name) = &record.fields[2].type_ref else {
706
+ panic!("union type");
707
+ };
708
+ let TypeDef::Union(union) = &ir.types[union_name] else {
709
+ panic!("union declaration");
710
+ };
711
+ assert!(union.is_lexical());
712
+ assert_eq!(union.branches.len(), 2);
713
+ assert_eq!(record.fields[3].type_ref, TypeRef::string());
714
+ assert_eq!(record.fields[4].type_ref, TypeRef::string());
715
+ let invalid = std::fs::read_to_string(&root)
716
+ .unwrap()
717
+ .replace("a:code", "a:missing");
718
+ std::fs::write(&root, invalid).unwrap();
719
+ assert!(XsdParser::new()
720
+ .parse_file(&root)
721
+ .unwrap_err()
722
+ .to_string()
723
+ .contains("Unresolved type"));
724
+ }
725
+
726
+ #[test]
727
+ fn schema_for_schemas_declared_types_are_not_unknown_builtins() {
728
+ let xsd = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.w3.org/2001/XMLSchema">
729
+ <xs:complexType name="Derived"><xs:complexContent><xs:extension base="xs:openAttrs"><xs:attribute name="name" type="xs:string"/></xs:extension></xs:complexContent></xs:complexType>
730
+ <xs:complexType name="openAttrs"><xs:anyAttribute processContents="lax"/></xs:complexType>
731
+ <xs:element name="Root" type="xs:Derived"/>
732
+ </xs:schema>"#;
733
+ let ir = XsdParser::new().parse_str(xsd).unwrap();
734
+ let ns = Some("http://www.w3.org/2001/XMLSchema");
735
+ let TypeDef::Struct(derived) = &ir.types[&QName::new(ns, "Derived")] else {
736
+ panic!("Derived");
737
+ };
738
+ assert_eq!(derived.base_type, Some(QName::new(ns, "openAttrs")));
739
+ let missing = xsd.replace("base=\"xs:openAttrs\"", "base=\"xs:Misspelled\"");
740
+ assert!(XsdParser::new()
741
+ .parse_str(&missing)
742
+ .unwrap_err()
743
+ .to_string()
744
+ .contains("Misspelled"));
745
+ let normal = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"><xs:element name="Root" type="xs:NotAType"/></xs:schema>"#;
746
+ assert!(XsdParser::new()
747
+ .parse_str(normal)
748
+ .unwrap_err()
749
+ .to_string()
750
+ .contains("Unknown built-in type"));
751
+ }
752
+
753
+ #[test]
754
+ fn inline_list_item_types_preserve_enum_and_union_declarations() {
755
+ for item in [
756
+ r#"<xs:restriction base="xs:string"><xs:enumeration value="one"/><xs:enumeration value="two"/></xs:restriction>"#,
757
+ r#"<xs:union memberTypes="xs:int xs:string"/>"#,
758
+ ] {
759
+ let xsd = format!(
760
+ r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"><xs:simpleType name="Values"><xs:list><xs:annotation><xs:documentation>items</xs:documentation></xs:annotation><xs:simpleType>{item}</xs:simpleType></xs:list></xs:simpleType></xs:schema>"#
761
+ );
762
+ let ir = XsdParser::new().parse_str(&xsd).unwrap();
763
+ let TypeDef::Simple(values) = &ir.types[&QName::new(None::<String>, "Values")] else {
764
+ panic!("list");
765
+ };
766
+ let TypeRef::List(inner) = &values.base_type else {
767
+ panic!("list reference");
768
+ };
769
+ let TypeRef::Named(name) = inner.as_ref() else {
770
+ panic!("item declaration");
771
+ };
772
+ match &ir.types[name] {
773
+ TypeDef::Enum(e) => assert_eq!(e.variants.len(), 2),
774
+ TypeDef::Union(u) => {
775
+ assert!(u.is_lexical());
776
+ assert_eq!(u.branches.len(), 2);
777
+ }
778
+ other => panic!("unexpected item: {other:?}"),
779
+ }
780
+ }
781
+ for list in ["<xs:list/>", "<xs:list></xs:list>"] {
782
+ let invalid = format!(
783
+ r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"><xs:simpleType name="Values">{list}</xs:simpleType></xs:schema>"#
784
+ );
785
+ assert!(XsdParser::new()
786
+ .parse_str(&invalid)
787
+ .unwrap_err()
788
+ .to_string()
789
+ .contains("xs:list requires"));
790
+ }
791
+ }
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "polyxml"
7
- version = "0.34.2"
7
+ version = "0.34.3"
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