polyxml 0.33.2__tar.gz → 0.33.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.
- {polyxml-0.33.2 → polyxml-0.33.4}/Cargo.lock +6 -6
- {polyxml-0.33.2 → polyxml-0.33.4}/Cargo.toml +1 -1
- {polyxml-0.33.2 → polyxml-0.33.4}/PKG-INFO +1 -1
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/src/codegen/go/mod.rs +349 -17
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/src/schema_parser/mod.rs +41 -16
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/tests/test_go_codegen.rs +241 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/pyproject.toml +1 -1
- {polyxml-0.33.2 → polyxml-0.33.4}/README.md +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/Cargo.toml +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/README.md +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/benches/core_benchmarks.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/benches/support/tag_dispatch_fixtures.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/benches/tag_dispatch.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/src/codegen/cpp/mod.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/src/codegen/csharp/mod.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/src/codegen/java/codec.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/src/codegen/java/mod.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/src/codegen/java/models.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/src/codegen/mod.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/src/codegen/python/aot.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/src/codegen/python/mod.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/src/codegen/rust/mod.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/src/codegen/typescript/mod.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/src/converters.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/src/error.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/src/ir/chunker.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/src/ir/mod.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/src/ir/tarjan.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/src/json.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/src/lib.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/src/parser.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/src/schema.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/src/serializer.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/src/transcoder.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/src/value.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/tests/fixtures/lexical_union.xsd +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/tests/fixtures/mixed_content.xsd +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/tests/fixtures/ts_mixed_content_inheritance.xsd +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/tests/test_adversarial_identifiers.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/tests/test_any_attribute.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/tests/test_any_element.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/tests/test_conformance.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/tests/test_core.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/tests/test_cpp_codegen.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/tests/test_csharp_codegen.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/tests/test_java_codegen.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/tests/test_json.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/tests/test_lexical_union_codegen.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/tests/test_mixed_content.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/tests/test_pattern_codegen.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/tests/test_python_codegen.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/tests/test_rust_codecs.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/tests/test_rust_codegen.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/tests/test_schema_audit.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/tests/test_schema_ir.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/tests/test_security.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/tests/test_transcoder.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/tests/test_ts_codegen.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/tests/test_unbounded_choice.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/tests/test_xsi_type.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-python/Cargo.toml +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-python/README.md +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-python/benches/aot_vs_dataclass.py +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-python/src/lib.rs +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-python/tests/test_conformance.py +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-python/tests/test_generated_models.py +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-python/tests/test_json.py +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-python/tests/test_mixed_content.py +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-python/tests/test_polyxml.py +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-python/tests/test_xsi_type.py +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/python/polyxml/AGENT_GUIDE.md +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/python/polyxml/__init__.py +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/python/polyxml/compat/__init__.py +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/python/polyxml/compat/xsdata.py +0 -0
- {polyxml-0.33.2 → polyxml-0.33.4}/python/polyxml/py.typed +0 -0
|
@@ -608,7 +608,7 @@ dependencies = [
|
|
|
608
608
|
|
|
609
609
|
[[package]]
|
|
610
610
|
name = "polyxml"
|
|
611
|
-
version = "0.33.
|
|
611
|
+
version = "0.33.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.33.
|
|
632
|
+
version = "0.33.4"
|
|
633
633
|
dependencies = [
|
|
634
634
|
"polyxml",
|
|
635
635
|
]
|
|
636
636
|
|
|
637
637
|
[[package]]
|
|
638
638
|
name = "polyxml-cli"
|
|
639
|
-
version = "0.33.
|
|
639
|
+
version = "0.33.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.33.
|
|
653
|
+
version = "0.33.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.33.
|
|
663
|
+
version = "0.33.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.33.
|
|
673
|
+
version = "0.33.4"
|
|
674
674
|
dependencies = [
|
|
675
675
|
"polyxml",
|
|
676
676
|
"quick-xml",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
use std::collections::HashSet;
|
|
2
2
|
use std::fmt::Write as FmtWrite;
|
|
3
3
|
|
|
4
|
-
use heck::{AsPascalCase, AsSnakeCase};
|
|
4
|
+
use heck::{AsLowerCamelCase, AsPascalCase, AsSnakeCase};
|
|
5
5
|
use serde::{Deserialize, Serialize};
|
|
6
6
|
|
|
7
7
|
use crate::codegen::{
|
|
@@ -361,7 +361,7 @@ impl GoCodegen {
|
|
|
361
361
|
{
|
|
362
362
|
imports.push("\"bytes\"");
|
|
363
363
|
}
|
|
364
|
-
if has_fmt {
|
|
364
|
+
if has_fmt || body.contains("fmt.") {
|
|
365
365
|
imports.push("\"fmt\"");
|
|
366
366
|
}
|
|
367
367
|
if has_io {
|
|
@@ -374,13 +374,12 @@ impl GoCodegen {
|
|
|
374
374
|
.types
|
|
375
375
|
.values()
|
|
376
376
|
.any(|def| matches!(def, TypeDef::Union(u) if u.is_lexical()))
|
|
377
|
+
&& body.contains("strconv.")
|
|
377
378
|
{
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
imports.push("\"strings\"");
|
|
383
|
-
}
|
|
379
|
+
imports.push("\"strconv\"");
|
|
380
|
+
}
|
|
381
|
+
if body.contains("strings.") {
|
|
382
|
+
imports.push("\"strings\"");
|
|
384
383
|
}
|
|
385
384
|
if has_time {
|
|
386
385
|
imports.push("\"time\"");
|
|
@@ -587,7 +586,11 @@ impl GoCodegen {
|
|
|
587
586
|
let mapped_type = self.context.map_type_ref(&branch.type_ref);
|
|
588
587
|
let mut tag_parts = Vec::new();
|
|
589
588
|
if self.options.emit_xml_tags {
|
|
590
|
-
|
|
589
|
+
let branch_xml = match &branch.namespace {
|
|
590
|
+
Some(ns) if !ns.is_empty() => format!("{} {}", ns, branch.xml_name),
|
|
591
|
+
_ => branch.xml_name.clone(),
|
|
592
|
+
};
|
|
593
|
+
tag_parts.push(format!("xml:\"{},omitempty\"", branch_xml));
|
|
591
594
|
}
|
|
592
595
|
if self.options.emit_json_tags {
|
|
593
596
|
tag_parts.push(format!("json:\"{},omitempty\"", branch.xml_name));
|
|
@@ -913,6 +916,11 @@ impl GoCodegen {
|
|
|
913
916
|
}
|
|
914
917
|
|
|
915
918
|
fn emit_struct(&self, out: &mut String, s: &StructDef, ir: &SchemaIR) {
|
|
919
|
+
if s.is_abstract {
|
|
920
|
+
self.emit_abstract_struct(out, s, ir);
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
|
|
916
924
|
let struct_name = type_ident(&s.qname);
|
|
917
925
|
if let Some(ref doc) = s.documentation {
|
|
918
926
|
for line in doc.lines() {
|
|
@@ -945,9 +953,13 @@ impl GoCodegen {
|
|
|
945
953
|
// Struct composition / inheritance if base struct exists
|
|
946
954
|
let mut simple_content_base: Option<String> = None;
|
|
947
955
|
if let Some(ref base_qname) = s.base_type {
|
|
948
|
-
if
|
|
956
|
+
if let Some(TypeDef::Struct(base_s)) = ir.types.get(base_qname) {
|
|
949
957
|
let base_name = type_ident(base_qname);
|
|
950
|
-
|
|
958
|
+
if base_s.is_abstract {
|
|
959
|
+
writeln!(out, " {}Base", base_name).unwrap();
|
|
960
|
+
} else {
|
|
961
|
+
writeln!(out, " {}", base_name).unwrap();
|
|
962
|
+
}
|
|
951
963
|
} else if let Some(prim) = PrimitiveType::from_xsd_name(&base_qname.local) {
|
|
952
964
|
simple_content_base = Some(self.context.map_primitive(prim).to_string());
|
|
953
965
|
} else if let Some(TypeDef::Simple(st)) = ir.types.get(base_qname) {
|
|
@@ -1018,7 +1030,7 @@ impl GoCodegen {
|
|
|
1018
1030
|
}
|
|
1019
1031
|
|
|
1020
1032
|
let field_type = self.resolve_field_type(f);
|
|
1021
|
-
let tag = self.build_field_struct_tags(f, &mut seen_json);
|
|
1033
|
+
let tag = self.build_field_struct_tags(f, s, ir, &mut seen_json);
|
|
1022
1034
|
writeln!(out, " {} {}{}", field_name, field_type, tag).unwrap();
|
|
1023
1035
|
}
|
|
1024
1036
|
|
|
@@ -1136,29 +1148,44 @@ impl GoCodegen {
|
|
|
1136
1148
|
}
|
|
1137
1149
|
}
|
|
1138
1150
|
|
|
1139
|
-
fn build_field_struct_tags(
|
|
1151
|
+
fn build_field_struct_tags(
|
|
1152
|
+
&self,
|
|
1153
|
+
f: &FieldDef,
|
|
1154
|
+
s: &StructDef,
|
|
1155
|
+
ir: &SchemaIR,
|
|
1156
|
+
seen_json: &mut HashSet<String>,
|
|
1157
|
+
) -> String {
|
|
1140
1158
|
let mut parts = Vec::new();
|
|
1141
1159
|
|
|
1142
1160
|
if self.options.emit_xml_tags {
|
|
1143
1161
|
let is_opt = f.cardinality.is_optional() || f.nillable;
|
|
1144
1162
|
let xml_val = match f.kind {
|
|
1145
1163
|
FieldKind::Attribute => {
|
|
1164
|
+
let attr_name = match &f.namespace {
|
|
1165
|
+
Some(ns) if !ns.is_empty() => format!("{} {}", ns, f.xml_name),
|
|
1166
|
+
_ => f.xml_name.clone(),
|
|
1167
|
+
};
|
|
1146
1168
|
if is_opt {
|
|
1147
|
-
format!("{},attr,omitempty",
|
|
1169
|
+
format!("{},attr,omitempty", attr_name)
|
|
1148
1170
|
} else {
|
|
1149
|
-
format!("{},attr",
|
|
1171
|
+
format!("{},attr", attr_name)
|
|
1150
1172
|
}
|
|
1151
1173
|
}
|
|
1152
1174
|
FieldKind::Text => ",chardata".to_string(),
|
|
1153
1175
|
FieldKind::Any => ",any".to_string(),
|
|
1154
1176
|
FieldKind::AnyAttribute => ",any,attr".to_string(),
|
|
1155
1177
|
FieldKind::Element => {
|
|
1178
|
+
let elem_name = if is_namespaced_ref(f, s, ir) {
|
|
1179
|
+
format!("{} {}", f.namespace.as_ref().unwrap(), f.xml_name)
|
|
1180
|
+
} else {
|
|
1181
|
+
f.xml_name.clone()
|
|
1182
|
+
};
|
|
1156
1183
|
if f.xml_name.is_empty() {
|
|
1157
1184
|
",any".to_string()
|
|
1158
1185
|
} else if is_opt {
|
|
1159
|
-
format!("{},omitempty",
|
|
1186
|
+
format!("{},omitempty", elem_name)
|
|
1160
1187
|
} else {
|
|
1161
|
-
|
|
1188
|
+
elem_name
|
|
1162
1189
|
}
|
|
1163
1190
|
}
|
|
1164
1191
|
};
|
|
@@ -1270,6 +1297,264 @@ impl GoCodegen {
|
|
|
1270
1297
|
writeln!(out, "}}\n").unwrap();
|
|
1271
1298
|
}
|
|
1272
1299
|
|
|
1300
|
+
fn emit_abstract_struct(&self, out: &mut String, s: &StructDef, ir: &SchemaIR) {
|
|
1301
|
+
let struct_name = type_ident(&s.qname);
|
|
1302
|
+
let base_struct_name = format!("{}Base", struct_name);
|
|
1303
|
+
|
|
1304
|
+
if let Some(ref doc) = s.documentation {
|
|
1305
|
+
for line in doc.lines() {
|
|
1306
|
+
writeln!(out, "// {}", line).unwrap();
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
// 1. Base struct definition holding declared fields of the abstract type
|
|
1311
|
+
writeln!(out, "type {} struct {{", base_struct_name).unwrap();
|
|
1312
|
+
if let Some(ref base_qname) = s.base_type {
|
|
1313
|
+
if let Some(TypeDef::Struct(base_s)) = ir.types.get(base_qname) {
|
|
1314
|
+
let base_name = type_ident(base_qname);
|
|
1315
|
+
if base_s.is_abstract {
|
|
1316
|
+
writeln!(out, " {}Base", base_name).unwrap();
|
|
1317
|
+
} else {
|
|
1318
|
+
writeln!(out, " {}", base_name).unwrap();
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
let mut seen_fields = HashSet::new();
|
|
1324
|
+
let field_names: Vec<String> = s
|
|
1325
|
+
.fields
|
|
1326
|
+
.iter()
|
|
1327
|
+
.map(|f| self.unique_field_name(&f.name, &mut seen_fields))
|
|
1328
|
+
.collect();
|
|
1329
|
+
|
|
1330
|
+
let mut seen_json = HashSet::new();
|
|
1331
|
+
for (f, field_name) in s.fields.iter().zip(&field_names) {
|
|
1332
|
+
let field_type = self.resolve_field_type(f);
|
|
1333
|
+
let tag = self.build_field_struct_tags(f, s, ir, &mut seen_json);
|
|
1334
|
+
writeln!(out, " {} {}{}", field_name, field_type, tag).unwrap();
|
|
1335
|
+
}
|
|
1336
|
+
writeln!(out, "}}\n").unwrap();
|
|
1337
|
+
|
|
1338
|
+
writeln!(
|
|
1339
|
+
out,
|
|
1340
|
+
"func (s {}) Validate() error {{\n return nil\n}}\n",
|
|
1341
|
+
base_struct_name
|
|
1342
|
+
)
|
|
1343
|
+
.unwrap();
|
|
1344
|
+
|
|
1345
|
+
// 2. Discover concrete derivations
|
|
1346
|
+
let derivations = concrete_derivations(s, ir);
|
|
1347
|
+
|
|
1348
|
+
// 3. Dispatch wrapper struct
|
|
1349
|
+
writeln!(out, "type {} struct {{", struct_name).unwrap();
|
|
1350
|
+
if self.options.emit_xml_tags {
|
|
1351
|
+
let mut xml_tags = Vec::new();
|
|
1352
|
+
if self.options.emit_json_tags {
|
|
1353
|
+
xml_tags.push("json:\"-\"");
|
|
1354
|
+
}
|
|
1355
|
+
if self.options.backend == GoBackend::Sonic {
|
|
1356
|
+
xml_tags.push("sonic:\"-\"");
|
|
1357
|
+
}
|
|
1358
|
+
if xml_tags.is_empty() {
|
|
1359
|
+
writeln!(out, " XMLName xml.Name").unwrap();
|
|
1360
|
+
} else {
|
|
1361
|
+
writeln!(out, " XMLName xml.Name `{}`", xml_tags.join(" ")).unwrap();
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
writeln!(out, " {}", base_struct_name).unwrap();
|
|
1365
|
+
|
|
1366
|
+
let mut seen_derives = HashSet::new();
|
|
1367
|
+
let derive_fields: Vec<(String, String, String)> = derivations
|
|
1368
|
+
.iter()
|
|
1369
|
+
.map(|d| {
|
|
1370
|
+
let type_name = type_ident(&d.qname);
|
|
1371
|
+
let field_name = self.unique_field_name(&type_name, &mut seen_derives);
|
|
1372
|
+
(d.qname.local.clone(), field_name, type_name)
|
|
1373
|
+
})
|
|
1374
|
+
.collect();
|
|
1375
|
+
|
|
1376
|
+
for (_local, field_name, type_name) in &derive_fields {
|
|
1377
|
+
let json_name = AsLowerCamelCase(field_name).to_string();
|
|
1378
|
+
writeln!(
|
|
1379
|
+
out,
|
|
1380
|
+
" {} *{} `xml:\"-\" json:\"{},omitempty\"`",
|
|
1381
|
+
field_name, type_name, json_name
|
|
1382
|
+
)
|
|
1383
|
+
.unwrap();
|
|
1384
|
+
}
|
|
1385
|
+
writeln!(out, "}}\n").unwrap();
|
|
1386
|
+
|
|
1387
|
+
// 4. Methods on dispatch wrapper
|
|
1388
|
+
writeln!(out, "func (s {}) Selected() string {{", struct_name).unwrap();
|
|
1389
|
+
for (local, field_name, _) in &derive_fields {
|
|
1390
|
+
writeln!(
|
|
1391
|
+
out,
|
|
1392
|
+
" if s.{} != nil {{ return {:?} }}",
|
|
1393
|
+
field_name, local
|
|
1394
|
+
)
|
|
1395
|
+
.unwrap();
|
|
1396
|
+
}
|
|
1397
|
+
writeln!(out, " return \"\"\n}}\n").unwrap();
|
|
1398
|
+
|
|
1399
|
+
writeln!(out, "func (s {}) Value() any {{", struct_name).unwrap();
|
|
1400
|
+
for (_, field_name, _) in &derive_fields {
|
|
1401
|
+
writeln!(
|
|
1402
|
+
out,
|
|
1403
|
+
" if s.{} != nil {{ return s.{} }}",
|
|
1404
|
+
field_name, field_name
|
|
1405
|
+
)
|
|
1406
|
+
.unwrap();
|
|
1407
|
+
}
|
|
1408
|
+
writeln!(out, " return nil\n}}\n").unwrap();
|
|
1409
|
+
|
|
1410
|
+
// UnmarshalXML
|
|
1411
|
+
writeln!(
|
|
1412
|
+
out,
|
|
1413
|
+
"func (s *{}) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {{",
|
|
1414
|
+
struct_name
|
|
1415
|
+
)
|
|
1416
|
+
.unwrap();
|
|
1417
|
+
writeln!(out, " s.XMLName = start.Name").unwrap();
|
|
1418
|
+
writeln!(out, " var typeAttr *xml.Attr").unwrap();
|
|
1419
|
+
writeln!(out, " for _, attr := range start.Attr {{").unwrap();
|
|
1420
|
+
writeln!(out, " if (attr.Name.Local == \"type\" && (attr.Name.Space == \"http://www.w3.org/2001/XMLSchema-instance\" || attr.Name.Space == \"xsi\")) || attr.Name.Local == \"xsi:type\" {{").unwrap();
|
|
1421
|
+
writeln!(out, " typeAttr = &attr").unwrap();
|
|
1422
|
+
writeln!(out, " break").unwrap();
|
|
1423
|
+
writeln!(out, " }}").unwrap();
|
|
1424
|
+
writeln!(out, " }}").unwrap();
|
|
1425
|
+
writeln!(out, " if typeAttr == nil {{").unwrap();
|
|
1426
|
+
writeln!(
|
|
1427
|
+
out,
|
|
1428
|
+
" return fmt.Errorf(\"abstract type '{}' requires xsi:type naming a concrete derivation\")",
|
|
1429
|
+
struct_name
|
|
1430
|
+
)
|
|
1431
|
+
.unwrap();
|
|
1432
|
+
writeln!(out, " }}").unwrap();
|
|
1433
|
+
|
|
1434
|
+
if derive_fields.is_empty() {
|
|
1435
|
+
writeln!(
|
|
1436
|
+
out,
|
|
1437
|
+
" return fmt.Errorf(\"abstract type '{}' has no registered derivations; deserialize the concrete type directly (escape hatch)\")",
|
|
1438
|
+
struct_name
|
|
1439
|
+
)
|
|
1440
|
+
.unwrap();
|
|
1441
|
+
} else {
|
|
1442
|
+
writeln!(out, " localType := typeAttr.Value").unwrap();
|
|
1443
|
+
writeln!(
|
|
1444
|
+
out,
|
|
1445
|
+
" if idx := strings.Index(localType, \":\"); idx != -1 {{"
|
|
1446
|
+
)
|
|
1447
|
+
.unwrap();
|
|
1448
|
+
writeln!(out, " localType = localType[idx+1:]").unwrap();
|
|
1449
|
+
writeln!(out, " }}").unwrap();
|
|
1450
|
+
writeln!(out, " switch localType {{").unwrap();
|
|
1451
|
+
for (local, field_name, type_name) in &derive_fields {
|
|
1452
|
+
writeln!(out, " case {:?}:", local).unwrap();
|
|
1453
|
+
writeln!(out, " var concrete {}", type_name).unwrap();
|
|
1454
|
+
writeln!(
|
|
1455
|
+
out,
|
|
1456
|
+
" if err := d.DecodeElement(&concrete, &start); err != nil {{ return err }}"
|
|
1457
|
+
)
|
|
1458
|
+
.unwrap();
|
|
1459
|
+
writeln!(out, " s.{} = &concrete", field_name).unwrap();
|
|
1460
|
+
writeln!(
|
|
1461
|
+
out,
|
|
1462
|
+
" s.{} = concrete.{}",
|
|
1463
|
+
base_struct_name, base_struct_name
|
|
1464
|
+
)
|
|
1465
|
+
.unwrap();
|
|
1466
|
+
writeln!(out, " return nil").unwrap();
|
|
1467
|
+
}
|
|
1468
|
+
let known = derive_fields
|
|
1469
|
+
.iter()
|
|
1470
|
+
.map(|(local, _, _)| local.as_str())
|
|
1471
|
+
.collect::<Vec<_>>()
|
|
1472
|
+
.join(", ");
|
|
1473
|
+
writeln!(out, " default:").unwrap();
|
|
1474
|
+
writeln!(
|
|
1475
|
+
out,
|
|
1476
|
+
" return fmt.Errorf(\"xsi:type=%q does not match any known derivation of '{}' (known: {})\", typeAttr.Value)",
|
|
1477
|
+
struct_name, known
|
|
1478
|
+
)
|
|
1479
|
+
.unwrap();
|
|
1480
|
+
writeln!(out, " }}").unwrap();
|
|
1481
|
+
}
|
|
1482
|
+
writeln!(out, "}}\n").unwrap();
|
|
1483
|
+
|
|
1484
|
+
// MarshalXML
|
|
1485
|
+
writeln!(
|
|
1486
|
+
out,
|
|
1487
|
+
"func (s {}) MarshalXML(e *xml.Encoder, start xml.StartElement) error {{",
|
|
1488
|
+
struct_name
|
|
1489
|
+
)
|
|
1490
|
+
.unwrap();
|
|
1491
|
+
writeln!(
|
|
1492
|
+
out,
|
|
1493
|
+
" if s.XMLName.Local != \"\" {{ start.Name = s.XMLName }}"
|
|
1494
|
+
)
|
|
1495
|
+
.unwrap();
|
|
1496
|
+
for (local, field_name, _) in &derive_fields {
|
|
1497
|
+
writeln!(out, " if s.{} != nil {{", field_name).unwrap();
|
|
1498
|
+
writeln!(out, " hasXsiNs := false\n hasType := false").unwrap();
|
|
1499
|
+
writeln!(out, " for _, attr := range start.Attr {{").unwrap();
|
|
1500
|
+
writeln!(out, " if attr.Name.Local == \"xmlns:xsi\" || (attr.Name.Local == \"xsi\" && attr.Name.Space == \"xmlns\") {{ hasXsiNs = true }}").unwrap();
|
|
1501
|
+
writeln!(out, " if attr.Name.Local == \"xsi:type\" || (attr.Name.Local == \"type\" && attr.Name.Space == \"http://www.w3.org/2001/XMLSchema-instance\") {{ hasType = true }}").unwrap();
|
|
1502
|
+
writeln!(out, " }}").unwrap();
|
|
1503
|
+
writeln!(out, " if !hasXsiNs {{ start.Attr = append(start.Attr, xml.Attr{{Name: xml.Name{{Local: \"xmlns:xsi\"}}, Value: \"http://www.w3.org/2001/XMLSchema-instance\"}}) }}").unwrap();
|
|
1504
|
+
writeln!(out, " if !hasType {{ start.Attr = append(start.Attr, xml.Attr{{Name: xml.Name{{Local: \"xsi:type\"}}, Value: {:?}}}) }}", local).unwrap();
|
|
1505
|
+
writeln!(
|
|
1506
|
+
out,
|
|
1507
|
+
" return e.EncodeElement(s.{}, start)",
|
|
1508
|
+
field_name
|
|
1509
|
+
)
|
|
1510
|
+
.unwrap();
|
|
1511
|
+
writeln!(out, " }}").unwrap();
|
|
1512
|
+
}
|
|
1513
|
+
if derive_fields.is_empty() {
|
|
1514
|
+
writeln!(
|
|
1515
|
+
out,
|
|
1516
|
+
" return fmt.Errorf(\"abstract type '{}' has no registered derivations\")",
|
|
1517
|
+
struct_name
|
|
1518
|
+
)
|
|
1519
|
+
.unwrap();
|
|
1520
|
+
} else {
|
|
1521
|
+
writeln!(
|
|
1522
|
+
out,
|
|
1523
|
+
" return fmt.Errorf(\"cannot marshal abstract type '{}' without a concrete derivation\")",
|
|
1524
|
+
struct_name
|
|
1525
|
+
)
|
|
1526
|
+
.unwrap();
|
|
1527
|
+
}
|
|
1528
|
+
writeln!(out, "}}\n").unwrap();
|
|
1529
|
+
|
|
1530
|
+
// Validate()
|
|
1531
|
+
writeln!(out, "func (s {}) Validate() error {{", struct_name).unwrap();
|
|
1532
|
+
for (_, field_name, _) in &derive_fields {
|
|
1533
|
+
writeln!(
|
|
1534
|
+
out,
|
|
1535
|
+
" if s.{} != nil {{ return s.{}.Validate() }}",
|
|
1536
|
+
field_name, field_name
|
|
1537
|
+
)
|
|
1538
|
+
.unwrap();
|
|
1539
|
+
}
|
|
1540
|
+
if derive_fields.is_empty() {
|
|
1541
|
+
writeln!(
|
|
1542
|
+
out,
|
|
1543
|
+
" return fmt.Errorf(\"abstract type '{}' has no registered derivations\")",
|
|
1544
|
+
struct_name
|
|
1545
|
+
)
|
|
1546
|
+
.unwrap();
|
|
1547
|
+
} else {
|
|
1548
|
+
writeln!(
|
|
1549
|
+
out,
|
|
1550
|
+
" return fmt.Errorf(\"abstract type '{}' requires a concrete derivation\")",
|
|
1551
|
+
struct_name
|
|
1552
|
+
)
|
|
1553
|
+
.unwrap();
|
|
1554
|
+
}
|
|
1555
|
+
writeln!(out, "}}\n").unwrap();
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1273
1558
|
fn emit_facet_checks(
|
|
1274
1559
|
&self,
|
|
1275
1560
|
out: &mut String,
|
|
@@ -1350,3 +1635,50 @@ impl GoCodegen {
|
|
|
1350
1635
|
}
|
|
1351
1636
|
}
|
|
1352
1637
|
}
|
|
1638
|
+
|
|
1639
|
+
fn derives_from(ir: &SchemaIR, d: &StructDef, base: &QName) -> bool {
|
|
1640
|
+
let mut seen: HashSet<QName> = HashSet::new();
|
|
1641
|
+
let mut cur = d.base_type.as_ref();
|
|
1642
|
+
while let Some(q) = cur {
|
|
1643
|
+
if q == base {
|
|
1644
|
+
return true;
|
|
1645
|
+
}
|
|
1646
|
+
if !seen.insert(q.clone()) {
|
|
1647
|
+
break;
|
|
1648
|
+
}
|
|
1649
|
+
cur = match ir.types.get(q) {
|
|
1650
|
+
Some(TypeDef::Struct(b)) => b.base_type.as_ref(),
|
|
1651
|
+
_ => None,
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1654
|
+
false
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
fn concrete_derivations<'a>(s: &'a StructDef, ir: &'a SchemaIR) -> Vec<&'a StructDef> {
|
|
1658
|
+
let mut result = Vec::new();
|
|
1659
|
+
for type_def in ir.types.values() {
|
|
1660
|
+
if let TypeDef::Struct(d) = type_def {
|
|
1661
|
+
if !d.is_abstract && d.qname != s.qname && derives_from(ir, d, &s.qname) {
|
|
1662
|
+
result.push(d);
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
result.sort_by(|a, b| a.qname.local.cmp(&b.qname.local));
|
|
1667
|
+
result
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
fn is_namespaced_ref(f: &FieldDef, s: &StructDef, ir: &SchemaIR) -> bool {
|
|
1671
|
+
let Some(ref ns) = f.namespace else {
|
|
1672
|
+
return false;
|
|
1673
|
+
};
|
|
1674
|
+
if ns.is_empty() {
|
|
1675
|
+
return false;
|
|
1676
|
+
}
|
|
1677
|
+
// If field namespace is different from struct namespace, it's definitely an external ref
|
|
1678
|
+
if s.qname.namespace.as_deref() != Some(ns.as_str()) {
|
|
1679
|
+
return true;
|
|
1680
|
+
}
|
|
1681
|
+
// If it matches a global element definition, it's an element ref
|
|
1682
|
+
let q = QName::new(Some(ns.as_str()), &f.xml_name);
|
|
1683
|
+
ir.elements.contains_key(&q)
|
|
1684
|
+
}
|
|
@@ -1805,16 +1805,29 @@ fn parse_element_field(
|
|
|
1805
1805
|
in_choice: bool,
|
|
1806
1806
|
in_unbounded_compositor: bool,
|
|
1807
1807
|
) -> Option<FieldDef> {
|
|
1808
|
-
let
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
let xml_name =
|
|
1812
|
-
|
|
1813
|
-
.unwrap_or_else(||
|
|
1808
|
+
let ref_attr = get_attr_value(e, "ref");
|
|
1809
|
+
let name_attr = get_attr_value(e, "name");
|
|
1810
|
+
|
|
1811
|
+
let (name, xml_name, namespace) = if let Some(ref r) = ref_attr {
|
|
1812
|
+
let qname = resolve_qname(r, target_ns, prefixes);
|
|
1813
|
+
let field_name = name_attr.unwrap_or_else(|| qname.local.clone());
|
|
1814
|
+
let xml_name = qname.local;
|
|
1815
|
+
let namespace = qname.namespace;
|
|
1816
|
+
(field_name, xml_name, namespace)
|
|
1817
|
+
} else {
|
|
1818
|
+
let n = name_attr?;
|
|
1819
|
+
let xml_name = n.clone();
|
|
1820
|
+
let namespace = target_ns.map(Into::into);
|
|
1821
|
+
(n, xml_name, namespace)
|
|
1822
|
+
};
|
|
1814
1823
|
|
|
1815
1824
|
let type_ref = get_attr_value(e, "type")
|
|
1816
1825
|
.map(|t| resolve_type_ref(&t, target_ns, prefixes))
|
|
1817
|
-
.or_else(||
|
|
1826
|
+
.or_else(|| {
|
|
1827
|
+
ref_attr
|
|
1828
|
+
.as_ref()
|
|
1829
|
+
.map(|r| resolve_type_ref(r, target_ns, prefixes))
|
|
1830
|
+
})
|
|
1818
1831
|
.unwrap_or(TypeRef::Primitive(PrimitiveType::String));
|
|
1819
1832
|
|
|
1820
1833
|
let min_occurs = if in_choice {
|
|
@@ -1845,7 +1858,7 @@ fn parse_element_field(
|
|
|
1845
1858
|
Some(FieldDef {
|
|
1846
1859
|
name: sanitize_field_name(&name),
|
|
1847
1860
|
xml_name,
|
|
1848
|
-
namespace
|
|
1861
|
+
namespace,
|
|
1849
1862
|
kind: FieldKind::Element,
|
|
1850
1863
|
type_ref,
|
|
1851
1864
|
cardinality: Cardinality {
|
|
@@ -1866,16 +1879,28 @@ fn parse_attribute_field(
|
|
|
1866
1879
|
target_ns: Option<&str>,
|
|
1867
1880
|
prefixes: &HashMap<String, String>,
|
|
1868
1881
|
) -> Option<FieldDef> {
|
|
1869
|
-
let
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
let xml_name =
|
|
1873
|
-
|
|
1874
|
-
.unwrap_or_else(||
|
|
1882
|
+
let ref_attr = get_attr_value(e, "ref");
|
|
1883
|
+
let name_attr = get_attr_value(e, "name");
|
|
1884
|
+
|
|
1885
|
+
let (name, xml_name, namespace) = if let Some(ref r) = ref_attr {
|
|
1886
|
+
let qname = resolve_qname(r, target_ns, prefixes);
|
|
1887
|
+
let field_name = name_attr.unwrap_or_else(|| qname.local.clone());
|
|
1888
|
+
let xml_name = qname.local;
|
|
1889
|
+
let namespace = qname.namespace;
|
|
1890
|
+
(field_name, xml_name, namespace)
|
|
1891
|
+
} else {
|
|
1892
|
+
let n = name_attr?;
|
|
1893
|
+
let xml_name = n.clone();
|
|
1894
|
+
(n, xml_name, None)
|
|
1895
|
+
};
|
|
1875
1896
|
|
|
1876
1897
|
let type_ref = get_attr_value(e, "type")
|
|
1877
1898
|
.map(|t| resolve_type_ref(&t, target_ns, prefixes))
|
|
1878
|
-
.or_else(||
|
|
1899
|
+
.or_else(|| {
|
|
1900
|
+
ref_attr
|
|
1901
|
+
.as_ref()
|
|
1902
|
+
.map(|r| resolve_type_ref(r, target_ns, prefixes))
|
|
1903
|
+
})
|
|
1879
1904
|
.unwrap_or(TypeRef::Primitive(PrimitiveType::String));
|
|
1880
1905
|
|
|
1881
1906
|
let is_required = get_attr_value(e, "use")
|
|
@@ -1894,7 +1919,7 @@ fn parse_attribute_field(
|
|
|
1894
1919
|
Some(FieldDef {
|
|
1895
1920
|
name: sanitize_field_name(&name),
|
|
1896
1921
|
xml_name,
|
|
1897
|
-
namespace
|
|
1922
|
+
namespace,
|
|
1898
1923
|
kind: FieldKind::Attribute,
|
|
1899
1924
|
type_ref,
|
|
1900
1925
|
cardinality,
|
|
@@ -837,3 +837,244 @@ fn test_go_any_attribute_codegen() {
|
|
|
837
837
|
|
|
838
838
|
assert!(code.contains("AnyAttribute []xml.Attr `xml:\",any,attr\" json:\"-\"`"));
|
|
839
839
|
}
|
|
840
|
+
|
|
841
|
+
#[test]
|
|
842
|
+
fn test_go_abstract_xsi_type_dispatch() {
|
|
843
|
+
let xsd = r#"<?xml version="1.0" encoding="UTF-8"?>
|
|
844
|
+
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:audit:abstract" xmlns:t="urn:audit:abstract" elementFormDefault="qualified">
|
|
845
|
+
<xs:complexType name="AbstractDocument" abstract="true">
|
|
846
|
+
<xs:sequence>
|
|
847
|
+
<xs:element name="Id" type="xs:string"/>
|
|
848
|
+
</xs:sequence>
|
|
849
|
+
</xs:complexType>
|
|
850
|
+
|
|
851
|
+
<xs:complexType name="InvoiceDocument">
|
|
852
|
+
<xs:complexContent>
|
|
853
|
+
<xs:extension base="t:AbstractDocument">
|
|
854
|
+
<xs:sequence>
|
|
855
|
+
<xs:element name="Amount" type="xs:decimal"/>
|
|
856
|
+
</xs:sequence>
|
|
857
|
+
</xs:extension>
|
|
858
|
+
</xs:complexContent>
|
|
859
|
+
</xs:complexType>
|
|
860
|
+
|
|
861
|
+
<xs:complexType name="ReceiptDocument">
|
|
862
|
+
<xs:complexContent>
|
|
863
|
+
<xs:extension base="t:AbstractDocument">
|
|
864
|
+
<xs:sequence>
|
|
865
|
+
<xs:element name="Store" type="xs:string"/>
|
|
866
|
+
</xs:sequence>
|
|
867
|
+
</xs:extension>
|
|
868
|
+
</xs:complexContent>
|
|
869
|
+
</xs:complexType>
|
|
870
|
+
|
|
871
|
+
<xs:element name="Document" type="t:AbstractDocument"/>
|
|
872
|
+
</xs:schema>"#;
|
|
873
|
+
|
|
874
|
+
let ir = XsdParser::new().parse_str(xsd).expect("parse failed");
|
|
875
|
+
let codegen = GoCodegen::new(GoOptions::default());
|
|
876
|
+
let code = codegen.generate_module(&ir);
|
|
877
|
+
|
|
878
|
+
assert!(code.contains("type AbstractDocumentBase struct"));
|
|
879
|
+
assert!(code.contains("type AbstractDocument struct"));
|
|
880
|
+
assert!(code.contains("InvoiceDocument *InvoiceDocument"));
|
|
881
|
+
assert!(code.contains("ReceiptDocument *ReceiptDocument"));
|
|
882
|
+
assert!(code.contains("func (s AbstractDocument) Selected() string"));
|
|
883
|
+
assert!(code.contains("func (s AbstractDocument) Value() any"));
|
|
884
|
+
assert!(code.contains("func (s *AbstractDocument) UnmarshalXML"));
|
|
885
|
+
assert!(code.contains("func (s AbstractDocument) MarshalXML"));
|
|
886
|
+
|
|
887
|
+
if Command::new("go").arg("version").output().is_err() {
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
let dir = tempdir().unwrap();
|
|
892
|
+
fs::write(
|
|
893
|
+
dir.path().join("go.mod"),
|
|
894
|
+
"module abstract_test\n\ngo 1.22\n",
|
|
895
|
+
)
|
|
896
|
+
.unwrap();
|
|
897
|
+
fs::write(dir.path().join("models.go"), &code).unwrap();
|
|
898
|
+
fs::write(
|
|
899
|
+
dir.path().join("models_test.go"),
|
|
900
|
+
r#"package models
|
|
901
|
+
|
|
902
|
+
import (
|
|
903
|
+
"encoding/xml"
|
|
904
|
+
"strings"
|
|
905
|
+
"testing"
|
|
906
|
+
)
|
|
907
|
+
|
|
908
|
+
func TestAbstractDocumentRoundTrip(t *testing.T) {
|
|
909
|
+
inputXml := `<Document xmlns="urn:audit:abstract" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="InvoiceDocument"><Id>INV-001</Id><Amount>199.99</Amount></Document>`
|
|
910
|
+
var doc Document
|
|
911
|
+
if err := xml.Unmarshal([]byte(inputXml), &doc); err != nil {
|
|
912
|
+
t.Fatalf("Unmarshal failed: %v", err)
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
if doc.Selected() != "InvoiceDocument" {
|
|
916
|
+
t.Fatalf("expected Selected() to be InvoiceDocument, got %q", doc.Selected())
|
|
917
|
+
}
|
|
918
|
+
if doc.InvoiceDocument == nil {
|
|
919
|
+
t.Fatalf("InvoiceDocument is nil")
|
|
920
|
+
}
|
|
921
|
+
if doc.InvoiceDocument.ID != "INV-001" {
|
|
922
|
+
t.Fatalf("expected ID 'INV-001', got %q", doc.InvoiceDocument.ID)
|
|
923
|
+
}
|
|
924
|
+
if doc.InvoiceDocument.Amount != 199.99 {
|
|
925
|
+
t.Fatalf("expected Amount 199.99, got %v", doc.InvoiceDocument.Amount)
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
marshaled, err := xml.Marshal(&doc)
|
|
929
|
+
if err != nil {
|
|
930
|
+
t.Fatalf("Marshal failed: %v", err)
|
|
931
|
+
}
|
|
932
|
+
marshaledStr := string(marshaled)
|
|
933
|
+
if !strings.Contains(marshaledStr, `xsi:type="InvoiceDocument"`) {
|
|
934
|
+
t.Fatalf("marshaled XML missing xsi:type: %s", marshaledStr)
|
|
935
|
+
}
|
|
936
|
+
if !strings.Contains(marshaledStr, `Amount`) {
|
|
937
|
+
t.Fatalf("marshaled XML missing Amount element: %s", marshaledStr)
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
// Missing xsi:type should fail
|
|
941
|
+
var missingDoc Document
|
|
942
|
+
if err := xml.Unmarshal([]byte(`<Document xmlns="urn:audit:abstract"><Id>INV-001</Id></Document>`), &missingDoc); err == nil {
|
|
943
|
+
t.Fatalf("expected error unmarshaling abstract type without xsi:type, got nil")
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
// Unknown xsi:type should fail
|
|
947
|
+
var unknownDoc Document
|
|
948
|
+
if err := xml.Unmarshal([]byte(`<Document xmlns="urn:audit:abstract" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="UnknownDoc"><Id>INV-001</Id></Document>`), &unknownDoc); err == nil {
|
|
949
|
+
t.Fatalf("expected error unmarshaling unknown xsi:type, got nil")
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
"#,
|
|
953
|
+
)
|
|
954
|
+
.unwrap();
|
|
955
|
+
|
|
956
|
+
let output = Command::new("go")
|
|
957
|
+
.args(["test", "-v", "./..."])
|
|
958
|
+
.current_dir(dir.path())
|
|
959
|
+
.output()
|
|
960
|
+
.expect("go test failed to execute");
|
|
961
|
+
|
|
962
|
+
assert!(
|
|
963
|
+
output.status.success(),
|
|
964
|
+
"go test failed: stdout:\n{}\nstderr:\n{}",
|
|
965
|
+
String::from_utf8_lossy(&output.stdout),
|
|
966
|
+
String::from_utf8_lossy(&output.stderr)
|
|
967
|
+
);
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
#[test]
|
|
971
|
+
fn test_go_namespaced_element_ref_matching() {
|
|
972
|
+
let schema = r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
|
973
|
+
xmlns:t="urn:audit:ref" targetNamespace="urn:audit:ref"
|
|
974
|
+
elementFormDefault="qualified">
|
|
975
|
+
<xs:element name="Child" type="xs:string"/>
|
|
976
|
+
<xs:element name="Container"><xs:complexType><xs:sequence>
|
|
977
|
+
<xs:element ref="t:Child"/>
|
|
978
|
+
</xs:sequence></xs:complexType></xs:element>
|
|
979
|
+
</xs:schema>"#;
|
|
980
|
+
|
|
981
|
+
let ir = XsdParser::new().parse_str(schema).unwrap();
|
|
982
|
+
let codegen = GoCodegen::new(GoOptions::default());
|
|
983
|
+
let code = codegen.generate_module(&ir);
|
|
984
|
+
|
|
985
|
+
// Code must NOT contain schema prefix t:Child
|
|
986
|
+
assert!(
|
|
987
|
+
!code.contains("\"t:Child\""),
|
|
988
|
+
"Go codegen must not emit schema lexical prefix in tags: {code}"
|
|
989
|
+
);
|
|
990
|
+
// Code must emit expanded namespace QName
|
|
991
|
+
assert!(
|
|
992
|
+
code.contains("`xml:\"urn:audit:ref Child\" json:\"Child\"`"),
|
|
993
|
+
"Go codegen must emit expanded QName and clean JSON name in tags: {code}"
|
|
994
|
+
);
|
|
995
|
+
|
|
996
|
+
if Command::new("go").arg("version").output().is_err() {
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
let dir = tempdir().unwrap();
|
|
1001
|
+
fs::write(dir.path().join("go.mod"), "module ref_test\n\ngo 1.22\n").unwrap();
|
|
1002
|
+
fs::write(dir.path().join("models.go"), &code).unwrap();
|
|
1003
|
+
fs::write(
|
|
1004
|
+
dir.path().join("models_test.go"),
|
|
1005
|
+
r#"package models
|
|
1006
|
+
|
|
1007
|
+
import (
|
|
1008
|
+
"encoding/xml"
|
|
1009
|
+
"strings"
|
|
1010
|
+
"testing"
|
|
1011
|
+
)
|
|
1012
|
+
|
|
1013
|
+
func TestNamespacedElementRef(t *testing.T) {
|
|
1014
|
+
// 1. Default namespace instance
|
|
1015
|
+
xml1 := `<Container xmlns="urn:audit:ref"><Child>abc</Child></Container>`
|
|
1016
|
+
var c1 Container
|
|
1017
|
+
if err := xml.Unmarshal([]byte(xml1), &c1); err != nil {
|
|
1018
|
+
t.Fatalf("unmarshal xml1 failed: %v", err)
|
|
1019
|
+
}
|
|
1020
|
+
if c1.Child != "abc" {
|
|
1021
|
+
t.Fatalf("expected Child 'abc', got %q", c1.Child)
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
// 2. Prefixed namespace instance (schema prefix t)
|
|
1025
|
+
xml2 := `<t:Container xmlns:t="urn:audit:ref"><t:Child>abc</t:Child></t:Container>`
|
|
1026
|
+
var c2 Container
|
|
1027
|
+
if err := xml.Unmarshal([]byte(xml2), &c2); err != nil {
|
|
1028
|
+
t.Fatalf("unmarshal xml2 failed: %v", err)
|
|
1029
|
+
}
|
|
1030
|
+
if c2.Child != "abc" {
|
|
1031
|
+
t.Fatalf("expected Child 'abc', got %q", c2.Child)
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
// 3. Alternate prefix instance (prefix p)
|
|
1035
|
+
xml3 := `<p:Container xmlns:p="urn:audit:ref"><p:Child>abc</p:Child></p:Container>`
|
|
1036
|
+
var c3 Container
|
|
1037
|
+
if err := xml.Unmarshal([]byte(xml3), &c3); err != nil {
|
|
1038
|
+
t.Fatalf("unmarshal xml3 failed: %v", err)
|
|
1039
|
+
}
|
|
1040
|
+
if c3.Child != "abc" {
|
|
1041
|
+
t.Fatalf("expected Child 'abc', got %q", c3.Child)
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
// 4. Foreign namespace URI with same local child name -> must NOT populate Child
|
|
1045
|
+
xml4 := `<Container xmlns="urn:audit:ref"><Child xmlns="urn:audit:other">abc</Child></Container>`
|
|
1046
|
+
var c4 Container
|
|
1047
|
+
if err := xml.Unmarshal([]byte(xml4), &c4); err != nil {
|
|
1048
|
+
t.Fatalf("unmarshal xml4 failed: %v", err)
|
|
1049
|
+
}
|
|
1050
|
+
if c4.Child != "" {
|
|
1051
|
+
t.Fatalf("expected Child to remain empty for foreign namespace, got %q", c4.Child)
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
// 5. Roundtrip serialization preserves namespace
|
|
1055
|
+
data, err := xml.Marshal(c1)
|
|
1056
|
+
if err != nil {
|
|
1057
|
+
t.Fatalf("marshal failed: %v", err)
|
|
1058
|
+
}
|
|
1059
|
+
dataStr := string(data)
|
|
1060
|
+
if !strings.Contains(dataStr, "Child") || !strings.Contains(dataStr, "urn:audit:ref") {
|
|
1061
|
+
t.Fatalf("marshaled XML missing Child or namespace: %s", dataStr)
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
"#,
|
|
1065
|
+
)
|
|
1066
|
+
.unwrap();
|
|
1067
|
+
|
|
1068
|
+
let output = Command::new("go")
|
|
1069
|
+
.args(["test", "-v", "./..."])
|
|
1070
|
+
.current_dir(dir.path())
|
|
1071
|
+
.output()
|
|
1072
|
+
.expect("go test failed to execute");
|
|
1073
|
+
|
|
1074
|
+
assert!(
|
|
1075
|
+
output.status.success(),
|
|
1076
|
+
"go test failed: stdout:\n{}\nstderr:\n{}",
|
|
1077
|
+
String::from_utf8_lossy(&output.stdout),
|
|
1078
|
+
String::from_utf8_lossy(&output.stderr)
|
|
1079
|
+
);
|
|
1080
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{polyxml-0.33.2 → polyxml-0.33.4}/crates/polyxml-core/benches/support/tag_dispatch_fixtures.rs
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|