polyxml 0.28.0__tar.gz → 0.29.0__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.28.0 → polyxml-0.29.0}/Cargo.lock +6 -6
- {polyxml-0.28.0 → polyxml-0.29.0}/Cargo.toml +1 -1
- {polyxml-0.28.0 → polyxml-0.29.0}/PKG-INFO +1 -1
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/src/codegen/csharp/mod.rs +128 -9
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/src/codegen/go/mod.rs +92 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/src/codegen/java/codec.rs +29 -4
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/src/codegen/java/mod.rs +13 -8
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/src/codegen/python/mod.rs +59 -1
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/src/codegen/rust/mod.rs +42 -5
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/src/ir/mod.rs +9 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/src/parser.rs +122 -7
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/src/schema.rs +62 -1
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/src/schema_parser/mod.rs +99 -1
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/src/serializer.rs +224 -104
- polyxml-0.29.0/crates/polyxml-core/tests/fixtures/mixed_content.xsd +17 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/tests/test_cpp_codegen.rs +7 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/tests/test_csharp_codegen.rs +4 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/tests/test_go_codegen.rs +5 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/tests/test_java_codegen.rs +2 -0
- polyxml-0.29.0/crates/polyxml-core/tests/test_mixed_content.rs +159 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/tests/test_python_codegen.rs +10 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/tests/test_rust_codegen.rs +15 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/tests/test_schema_audit.rs +1 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/tests/test_schema_ir.rs +4 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/tests/test_ts_codegen.rs +5 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-python/src/lib.rs +97 -2
- polyxml-0.29.0/crates/polyxml-python/tests/test_mixed_content.py +44 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/pyproject.toml +1 -1
- {polyxml-0.28.0 → polyxml-0.29.0}/README.md +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/Cargo.toml +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/README.md +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/benches/core_benchmarks.rs +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/benches/support/tag_dispatch_fixtures.rs +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/benches/tag_dispatch.rs +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/src/codegen/cpp/mod.rs +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/src/codegen/java/models.rs +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/src/codegen/mod.rs +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/src/codegen/python/aot.rs +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/src/codegen/typescript/mod.rs +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/src/converters.rs +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/src/error.rs +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/src/ir/tarjan.rs +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/src/json.rs +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/src/lib.rs +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/src/transcoder.rs +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/src/value.rs +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/tests/fixtures/lexical_union.xsd +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/tests/test_adversarial_identifiers.rs +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/tests/test_conformance.rs +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/tests/test_core.rs +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/tests/test_json.rs +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/tests/test_lexical_union_codegen.rs +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/tests/test_pattern_codegen.rs +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/tests/test_rust_codecs.rs +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/tests/test_security.rs +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/tests/test_transcoder.rs +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/tests/test_unbounded_choice.rs +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-core/tests/test_xsi_type.rs +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-python/Cargo.toml +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-python/README.md +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-python/benches/aot_vs_dataclass.py +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-python/tests/test_conformance.py +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-python/tests/test_generated_models.py +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-python/tests/test_json.py +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-python/tests/test_polyxml.py +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/crates/polyxml-python/tests/test_xsi_type.py +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/python/polyxml/AGENT_GUIDE.md +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/python/polyxml/__init__.py +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/python/polyxml/compat/__init__.py +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/python/polyxml/compat/xsdata.py +0 -0
- {polyxml-0.28.0 → polyxml-0.29.0}/python/polyxml/py.typed +0 -0
|
@@ -608,7 +608,7 @@ dependencies = [
|
|
|
608
608
|
|
|
609
609
|
[[package]]
|
|
610
610
|
name = "polyxml"
|
|
611
|
-
version = "0.
|
|
611
|
+
version = "0.29.0"
|
|
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.
|
|
632
|
+
version = "0.29.0"
|
|
633
633
|
dependencies = [
|
|
634
634
|
"polyxml",
|
|
635
635
|
]
|
|
636
636
|
|
|
637
637
|
[[package]]
|
|
638
638
|
name = "polyxml-cli"
|
|
639
|
-
version = "0.
|
|
639
|
+
version = "0.29.0"
|
|
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.
|
|
653
|
+
version = "0.29.0"
|
|
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.
|
|
663
|
+
version = "0.29.0"
|
|
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.
|
|
673
|
+
version = "0.29.0"
|
|
674
674
|
dependencies = [
|
|
675
675
|
"polyxml",
|
|
676
676
|
"quick-xml",
|
|
@@ -15,7 +15,7 @@ use crate::codegen::{
|
|
|
15
15
|
};
|
|
16
16
|
use crate::ir::{
|
|
17
17
|
EnumDef, FieldDef, FieldKind, PrimitiveType, QName, RestrictionFacets, SchemaIR, SimpleTypeDef,
|
|
18
|
-
StructDef, TypeDef, TypeRef, UnionDef,
|
|
18
|
+
StructDef, TypeDef, TypeRef, UnionBranch, UnionDef,
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
/// Record emission kind: class vs struct.
|
|
@@ -242,6 +242,15 @@ fn type_ident(q: &QName) -> String {
|
|
|
242
242
|
lookup_type_name(q, || to_csharp_type_name(&q.local))
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
+
fn choice_variant_name(branch: &UnionBranch) -> String {
|
|
246
|
+
let name = to_csharp_type_name(&branch.variant_name);
|
|
247
|
+
if matches!(&branch.type_ref, TypeRef::Named(qname) if type_ident(qname) == name) {
|
|
248
|
+
format!("{name}Branch")
|
|
249
|
+
} else {
|
|
250
|
+
name
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
245
254
|
impl CSharpCodegen {
|
|
246
255
|
/// Creates a new C# code generator with the given configuration options.
|
|
247
256
|
pub fn new(options: CSharpOptions) -> Self {
|
|
@@ -584,9 +593,9 @@ impl CSharpCodegen {
|
|
|
584
593
|
}
|
|
585
594
|
|
|
586
595
|
// XmlInclude attributes for element-choice polymorphism.
|
|
587
|
-
if self.options.emit_xml_attributes && !u.is_lexical() {
|
|
596
|
+
if self.options.emit_xml_attributes && !u.is_lexical() && !u.is_mixed_content() {
|
|
588
597
|
for branch in &u.branches {
|
|
589
|
-
let variant_name =
|
|
598
|
+
let variant_name = choice_variant_name(branch);
|
|
590
599
|
writeln!(
|
|
591
600
|
out,
|
|
592
601
|
"{}[XmlInclude(typeof({}.{}))]",
|
|
@@ -603,7 +612,7 @@ impl CSharpCodegen {
|
|
|
603
612
|
"{}[JsonDerivedType(typeof({}.{}), {:?})]",
|
|
604
613
|
indent,
|
|
605
614
|
choice_name,
|
|
606
|
-
|
|
615
|
+
choice_variant_name(branch),
|
|
607
616
|
branch.xml_name
|
|
608
617
|
)
|
|
609
618
|
.unwrap();
|
|
@@ -624,7 +633,7 @@ impl CSharpCodegen {
|
|
|
624
633
|
writeln!(out, "{}{{", indent).unwrap();
|
|
625
634
|
|
|
626
635
|
for branch in &u.branches {
|
|
627
|
-
let variant_name =
|
|
636
|
+
let variant_name = choice_variant_name(branch);
|
|
628
637
|
let branch_type = self.context.map_type_ref(&branch.type_ref);
|
|
629
638
|
|
|
630
639
|
if let Some(ref doc) = branch.documentation {
|
|
@@ -632,7 +641,7 @@ impl CSharpCodegen {
|
|
|
632
641
|
}
|
|
633
642
|
|
|
634
643
|
let mut branch_attrs = Vec::new();
|
|
635
|
-
if self.options.emit_xml_attributes && !u.is_lexical() {
|
|
644
|
+
if self.options.emit_xml_attributes && !u.is_lexical() && !u.is_mixed_content() {
|
|
636
645
|
branch_attrs.push(format!("XmlElement(\"{}\")", branch.xml_name));
|
|
637
646
|
}
|
|
638
647
|
if self.options.emit_json_attributes && !u.is_lexical() {
|
|
@@ -701,7 +710,7 @@ impl CSharpCodegen {
|
|
|
701
710
|
let _ = writeln!(out, "{} {{", indent);
|
|
702
711
|
let _ = writeln!(out, "{} var value = text.Trim();", indent);
|
|
703
712
|
for (idx, branch) in u.branches.iter().enumerate() {
|
|
704
|
-
let variant =
|
|
713
|
+
let variant = choice_variant_name(branch);
|
|
705
714
|
let mapped = self.context.map_type_ref(&branch.type_ref);
|
|
706
715
|
if let TypeRef::Named(qname) = &branch.type_ref {
|
|
707
716
|
if let Some(TypeDef::Enum(def)) = ir.types.get(qname) {
|
|
@@ -779,7 +788,7 @@ impl CSharpCodegen {
|
|
|
779
788
|
);
|
|
780
789
|
let _ = writeln!(out, "{} {{", indent);
|
|
781
790
|
for branch in &u.branches {
|
|
782
|
-
let variant =
|
|
791
|
+
let variant = choice_variant_name(branch);
|
|
783
792
|
let value = if matches!(
|
|
784
793
|
super::primitive_base(&branch.type_ref, ir),
|
|
785
794
|
TypeRef::Primitive(PrimitiveType::Date)
|
|
@@ -802,6 +811,111 @@ impl CSharpCodegen {
|
|
|
802
811
|
let _ = writeln!(out, "{} }};", indent);
|
|
803
812
|
}
|
|
804
813
|
|
|
814
|
+
fn emit_mixed_struct(
|
|
815
|
+
&self,
|
|
816
|
+
out: &mut String,
|
|
817
|
+
s: &StructDef,
|
|
818
|
+
ir: &SchemaIR,
|
|
819
|
+
indent: &str,
|
|
820
|
+
struct_name: &str,
|
|
821
|
+
prop_names: &[String],
|
|
822
|
+
) {
|
|
823
|
+
let Some((item_index, union)) =
|
|
824
|
+
s.fields
|
|
825
|
+
.iter()
|
|
826
|
+
.enumerate()
|
|
827
|
+
.find_map(|(index, field)| match &field.type_ref {
|
|
828
|
+
TypeRef::Named(qname) => match ir.types.get(qname) {
|
|
829
|
+
Some(TypeDef::Union(union)) if union.is_mixed_content() => {
|
|
830
|
+
Some((index, union))
|
|
831
|
+
}
|
|
832
|
+
_ => None,
|
|
833
|
+
},
|
|
834
|
+
_ => None,
|
|
835
|
+
})
|
|
836
|
+
else {
|
|
837
|
+
return;
|
|
838
|
+
};
|
|
839
|
+
let item_name = &prop_names[item_index];
|
|
840
|
+
let union_name = type_ident(&union.qname);
|
|
841
|
+
let class_kind = if self.options.use_records {
|
|
842
|
+
"record"
|
|
843
|
+
} else {
|
|
844
|
+
"class"
|
|
845
|
+
};
|
|
846
|
+
let _ = writeln!(out, "{indent}public {class_kind} {struct_name} : System.Xml.Serialization.IXmlSerializable\n{indent}{{");
|
|
847
|
+
for (field, prop_name) in s.fields.iter().zip(prop_names) {
|
|
848
|
+
let ty = self.map_field_type(field, ir);
|
|
849
|
+
if field.kind == FieldKind::Attribute {
|
|
850
|
+
if self.options.emit_json_attributes {
|
|
851
|
+
let _ = writeln!(out, "{indent} [JsonPropertyName({:?})]", field.xml_name);
|
|
852
|
+
}
|
|
853
|
+
let _ = writeln!(
|
|
854
|
+
out,
|
|
855
|
+
"{indent} public {ty} {prop_name} {{ get; set; }} = default!;"
|
|
856
|
+
);
|
|
857
|
+
} else if field.kind == FieldKind::Element && prop_name == item_name {
|
|
858
|
+
let _ = writeln!(out, "{indent} [XmlIgnore]\n{indent} public List<{union_name}> {item_name} {{ get; set; }} = new();");
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
let _ = writeln!(
|
|
862
|
+
out,
|
|
863
|
+
"{indent} public System.Xml.Schema.XmlSchema? GetSchema() => null;"
|
|
864
|
+
);
|
|
865
|
+
let _ = writeln!(
|
|
866
|
+
out,
|
|
867
|
+
"{indent} public void ReadXml(System.Xml.XmlReader reader)\n{indent} {{"
|
|
868
|
+
);
|
|
869
|
+
out.push_str(" reader.MoveToContent();\n");
|
|
870
|
+
for (field, prop_name) in s.fields.iter().zip(prop_names) {
|
|
871
|
+
if field.kind == FieldKind::Attribute {
|
|
872
|
+
let ty = self.map_field_type(field, ir);
|
|
873
|
+
let base_type = ty.trim_end_matches('?');
|
|
874
|
+
let _ = writeln!(out, " {{ var raw = reader.GetAttribute({:?}); if (raw is not null) {prop_name} = ({ty})System.Convert.ChangeType(raw, typeof({base_type}), System.Globalization.CultureInfo.InvariantCulture); }}", field.xml_name);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
let _ = writeln!(out, " {item_name} = new List<{union_name}>();");
|
|
878
|
+
out.push_str(" bool empty = reader.IsEmptyElement;\n reader.ReadStartElement();\n if (empty) return;\n while (reader.NodeType != System.Xml.XmlNodeType.EndElement && !reader.EOF)\n {\n switch (reader.NodeType)\n {\n case System.Xml.XmlNodeType.Text:\n case System.Xml.XmlNodeType.CDATA:\n case System.Xml.XmlNodeType.Whitespace:\n case System.Xml.XmlNodeType.SignificantWhitespace:\n");
|
|
879
|
+
let _ = writeln!(
|
|
880
|
+
out,
|
|
881
|
+
" {item_name}.Add(new {union_name}.Text(reader.Value));"
|
|
882
|
+
);
|
|
883
|
+
out.push_str(" reader.Read();\n break;\n case System.Xml.XmlNodeType.Element:\n switch (reader.LocalName)\n {\n");
|
|
884
|
+
for branch in &union.branches {
|
|
885
|
+
if branch.xml_name == "#text" {
|
|
886
|
+
continue;
|
|
887
|
+
}
|
|
888
|
+
let branch_type = self.context.map_type_ref(&branch.type_ref);
|
|
889
|
+
let variant = choice_variant_name(branch);
|
|
890
|
+
let namespace = branch.namespace.as_deref().unwrap_or("");
|
|
891
|
+
let _ = writeln!(out, " case {:?}:\n {{ var serializer = new XmlSerializer(typeof({branch_type}), new XmlRootAttribute({:?}) {{ Namespace = {:?} }}); {item_name}.Add(new {union_name}.{variant}(({branch_type})serializer.Deserialize(reader)!)); break; }}", branch.xml_name, branch.xml_name, namespace);
|
|
892
|
+
}
|
|
893
|
+
out.push_str(" default: reader.Skip(); break;\n }\n break;\n default: reader.Read(); break;\n }\n }\n reader.ReadEndElement();\n");
|
|
894
|
+
let _ = writeln!(out, "{indent} }}");
|
|
895
|
+
let _ = writeln!(
|
|
896
|
+
out,
|
|
897
|
+
"{indent} public void WriteXml(System.Xml.XmlWriter writer)\n{indent} {{"
|
|
898
|
+
);
|
|
899
|
+
for (field, prop_name) in s.fields.iter().zip(prop_names) {
|
|
900
|
+
if field.kind == FieldKind::Attribute {
|
|
901
|
+
let _ = writeln!(out, " if ({prop_name} is not null) writer.WriteAttributeString({:?}, System.Convert.ToString({prop_name}, System.Globalization.CultureInfo.InvariantCulture));", field.xml_name);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
let _ = writeln!(out, " foreach (var item in {item_name})\n {{\n switch (item)\n {{");
|
|
905
|
+
for branch in &union.branches {
|
|
906
|
+
let variant = choice_variant_name(branch);
|
|
907
|
+
if branch.xml_name == "#text" {
|
|
908
|
+
let _ = writeln!(out, " case {union_name}.{variant} text: writer.WriteString(text.Value); break;");
|
|
909
|
+
} else {
|
|
910
|
+
let branch_type = self.context.map_type_ref(&branch.type_ref);
|
|
911
|
+
let namespace = branch.namespace.as_deref().unwrap_or("");
|
|
912
|
+
let _ = writeln!(out, " case {union_name}.{variant} value: new XmlSerializer(typeof({branch_type}), new XmlRootAttribute({:?}) {{ Namespace = {:?} }}).Serialize(writer, value.Value); break;", branch.xml_name, namespace);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
out.push_str(" }\n }\n");
|
|
916
|
+
let _ = writeln!(out, "{indent} }}\n{indent}}}\n");
|
|
917
|
+
}
|
|
918
|
+
|
|
805
919
|
fn emit_struct(&self, out: &mut String, s: &StructDef, ir: &SchemaIR, indent: &str) {
|
|
806
920
|
let struct_name = type_ident(&s.qname);
|
|
807
921
|
if let Some(ref doc) = s.documentation {
|
|
@@ -852,6 +966,11 @@ impl CSharpCodegen {
|
|
|
852
966
|
.map(|f| self.unique_property_name(&f.name, &mut seen_props, Some(&struct_name)))
|
|
853
967
|
.collect();
|
|
854
968
|
|
|
969
|
+
if s.is_mixed && self.options.emit_xml_attributes {
|
|
970
|
+
self.emit_mixed_struct(out, s, ir, indent, &struct_name, &prop_names);
|
|
971
|
+
return;
|
|
972
|
+
}
|
|
973
|
+
|
|
855
974
|
let has_lexical_union = self.options.emit_xml_attributes && s.fields.iter().any(|f| {
|
|
856
975
|
matches!(&f.type_ref, TypeRef::Named(q) if matches!(ir.types.get(q), Some(TypeDef::Union(u)) if u.is_lexical()))
|
|
857
976
|
});
|
|
@@ -1232,7 +1351,7 @@ impl CSharpCodegen {
|
|
|
1232
1351
|
if let Some(TypeDef::Union(u)) = ir.types.get(qname) {
|
|
1233
1352
|
let choice_name = type_ident(&u.qname);
|
|
1234
1353
|
for branch in &u.branches {
|
|
1235
|
-
let variant_name =
|
|
1354
|
+
let variant_name = choice_variant_name(branch);
|
|
1236
1355
|
parts.push(format!(
|
|
1237
1356
|
"XmlElement(\"{}\", typeof({}.{}))",
|
|
1238
1357
|
branch.xml_name, choice_name, variant_name
|
|
@@ -279,6 +279,11 @@ impl GoCodegen {
|
|
|
279
279
|
for type_def in ir.types.values() {
|
|
280
280
|
match type_def {
|
|
281
281
|
TypeDef::Struct(s) => {
|
|
282
|
+
if s.is_mixed && self.options.emit_xml_tags {
|
|
283
|
+
has_xml = true;
|
|
284
|
+
has_fmt = true;
|
|
285
|
+
has_io = true;
|
|
286
|
+
}
|
|
282
287
|
for f in &s.fields {
|
|
283
288
|
if self.references_time(&f.type_ref) {
|
|
284
289
|
has_time = true;
|
|
@@ -352,6 +357,14 @@ impl GoCodegen {
|
|
|
352
357
|
if has_xml {
|
|
353
358
|
imports.push("\"encoding/xml\"");
|
|
354
359
|
}
|
|
360
|
+
if self.options.emit_xml_tags
|
|
361
|
+
&& ir
|
|
362
|
+
.types
|
|
363
|
+
.values()
|
|
364
|
+
.any(|def| matches!(def, TypeDef::Struct(s) if s.is_mixed))
|
|
365
|
+
{
|
|
366
|
+
imports.push("\"bytes\"");
|
|
367
|
+
}
|
|
355
368
|
if has_fmt {
|
|
356
369
|
imports.push("\"fmt\"");
|
|
357
370
|
}
|
|
@@ -977,11 +990,90 @@ impl GoCodegen {
|
|
|
977
990
|
|
|
978
991
|
writeln!(out, "}}\n").unwrap();
|
|
979
992
|
|
|
993
|
+
if s.is_mixed && self.options.emit_xml_tags {
|
|
994
|
+
self.emit_mixed_struct_xml(out, s, ir, &field_names);
|
|
995
|
+
}
|
|
996
|
+
|
|
980
997
|
if self.options.validate_facets {
|
|
981
998
|
self.emit_struct_validator(out, s, ir, &field_names);
|
|
982
999
|
}
|
|
983
1000
|
}
|
|
984
1001
|
|
|
1002
|
+
fn emit_mixed_struct_xml(
|
|
1003
|
+
&self,
|
|
1004
|
+
out: &mut String,
|
|
1005
|
+
s: &StructDef,
|
|
1006
|
+
ir: &SchemaIR,
|
|
1007
|
+
field_names: &[String],
|
|
1008
|
+
) {
|
|
1009
|
+
let Some((item_index, union)) =
|
|
1010
|
+
s.fields
|
|
1011
|
+
.iter()
|
|
1012
|
+
.enumerate()
|
|
1013
|
+
.find_map(|(index, field)| match &field.type_ref {
|
|
1014
|
+
TypeRef::Named(qname) => match ir.types.get(qname) {
|
|
1015
|
+
Some(TypeDef::Union(union)) if union.is_mixed_content() => {
|
|
1016
|
+
Some((index, union))
|
|
1017
|
+
}
|
|
1018
|
+
_ => None,
|
|
1019
|
+
},
|
|
1020
|
+
_ => None,
|
|
1021
|
+
})
|
|
1022
|
+
else {
|
|
1023
|
+
return;
|
|
1024
|
+
};
|
|
1025
|
+
let struct_name = type_ident(&s.qname);
|
|
1026
|
+
let item_field = &field_names[item_index];
|
|
1027
|
+
let union_name = type_ident(&union.qname);
|
|
1028
|
+
let mut seen = HashSet::new();
|
|
1029
|
+
let branch_fields = union
|
|
1030
|
+
.branches
|
|
1031
|
+
.iter()
|
|
1032
|
+
.map(|branch| self.unique_field_name(&branch.variant_name, &mut seen))
|
|
1033
|
+
.collect::<Vec<_>>();
|
|
1034
|
+
|
|
1035
|
+
let _ = writeln!(
|
|
1036
|
+
out,
|
|
1037
|
+
"func (v *{struct_name}) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {{"
|
|
1038
|
+
);
|
|
1039
|
+
let _ = writeln!(out, " type Alias {struct_name}");
|
|
1040
|
+
out.push_str(" var attrBytes bytes.Buffer\n attrEncoder := xml.NewEncoder(&attrBytes)\n if err := attrEncoder.EncodeToken(start); err != nil { return err }\n if err := attrEncoder.EncodeToken(start.End()); err != nil { return err }\n if err := attrEncoder.Flush(); err != nil { return err }\n var attrs Alias\n if err := xml.Unmarshal(attrBytes.Bytes(), &attrs); err != nil { return err }\n");
|
|
1041
|
+
let _ = writeln!(out, " *v = {struct_name}(attrs)");
|
|
1042
|
+
let _ = writeln!(out, " v.{item_field} = nil");
|
|
1043
|
+
out.push_str(" for {\n token, err := d.Token()\n if err != nil { if err == io.EOF { return io.ErrUnexpectedEOF }; return err }\n switch element := token.(type) {\n case xml.CharData:\n if len(element) > 0 {\n value := string(element)\n");
|
|
1044
|
+
let _ = writeln!(out, " v.{item_field} = append(v.{item_field}, {union_name}{{{text_field}: &value}})", text_field=branch_fields[0]);
|
|
1045
|
+
out.push_str(" }\n case xml.StartElement:\n switch element.Name.Local {\n");
|
|
1046
|
+
for (branch, branch_field) in union.branches.iter().zip(&branch_fields) {
|
|
1047
|
+
if branch.xml_name == "#text" {
|
|
1048
|
+
continue;
|
|
1049
|
+
}
|
|
1050
|
+
let mapped = self.context.map_type_ref(&branch.type_ref);
|
|
1051
|
+
let _ = writeln!(out, " case {:?}:\n var value {mapped}\n if err := d.DecodeElement(&value, &element); err != nil {{ return err }}\n v.{item_field} = append(v.{item_field}, {union_name}{{{branch_field}: &value}})", branch.xml_name);
|
|
1052
|
+
}
|
|
1053
|
+
out.push_str(" default:\n if err := d.Skip(); err != nil { return err }\n }\n case xml.EndElement:\n if element.Name == start.Name { return nil }\n }\n }\n}\n\n");
|
|
1054
|
+
|
|
1055
|
+
let _ = writeln!(
|
|
1056
|
+
out,
|
|
1057
|
+
"func (v {struct_name}) MarshalXML(e *xml.Encoder, start xml.StartElement) error {{"
|
|
1058
|
+
);
|
|
1059
|
+
let _ = writeln!(
|
|
1060
|
+
out,
|
|
1061
|
+
" type Alias {struct_name}\n attrs := Alias(v)\n attrs.{item_field} = nil"
|
|
1062
|
+
);
|
|
1063
|
+
out.push_str(" raw, err := xml.Marshal(attrs)\n if err != nil { return err }\n decoder := xml.NewDecoder(bytes.NewReader(raw))\n token, err := decoder.Token()\n if err != nil { return err }\n start.Attr = token.(xml.StartElement).Attr\n if err := e.EncodeToken(start); err != nil { return err }\n");
|
|
1064
|
+
let _ = writeln!(out, " for _, item := range v.{item_field} {{");
|
|
1065
|
+
for (branch, branch_field) in union.branches.iter().zip(&branch_fields) {
|
|
1066
|
+
let _ = writeln!(out, " if item.{branch_field} != nil {{");
|
|
1067
|
+
if branch.xml_name == "#text" {
|
|
1068
|
+
let _ = writeln!(out, " if err := e.EncodeToken(xml.CharData(*item.{branch_field})); err != nil {{ return err }}");
|
|
1069
|
+
} else {
|
|
1070
|
+
let _ = writeln!(out, " if err := e.EncodeElement(item.{branch_field}, xml.StartElement{{Name: xml.Name{{Local: {:?}}}}}); err != nil {{ return err }}", branch.xml_name);
|
|
1071
|
+
}
|
|
1072
|
+
out.push_str(" continue\n }\n");
|
|
1073
|
+
}
|
|
1074
|
+
out.push_str(" return fmt.Errorf(\"mixed content item has no selected branch\")\n }\n return e.EncodeToken(start.End())\n}\n\n");
|
|
1075
|
+
}
|
|
1076
|
+
|
|
985
1077
|
fn resolve_field_type(&self, f: &FieldDef) -> String {
|
|
986
1078
|
let base_type = self.context.map_type_ref(&f.type_ref);
|
|
987
1079
|
|
|
@@ -179,7 +179,7 @@ impl JavaCodegen {
|
|
|
179
179
|
out.push_str(" String text = reader.getElementText();\n");
|
|
180
180
|
for b in &u.branches {
|
|
181
181
|
let expr = self.parse_scalar(&b.type_ref, "text", ir);
|
|
182
|
-
let variant =
|
|
182
|
+
let variant = union_variant_name(b);
|
|
183
183
|
let _ = writeln!(out, " try {{ return new {name}.{variant}({expr}); }} catch (RuntimeException ignored) {{ }}");
|
|
184
184
|
}
|
|
185
185
|
let _ = writeln!(out, " throw new XMLStreamException(\"No {} union member accepts: \" + text);", name);
|
|
@@ -187,7 +187,7 @@ impl JavaCodegen {
|
|
|
187
187
|
let _=writeln!(out," {name} result = null;\n while (reader.hasNext()) {{\n int event = reader.next();\n if (event == XMLStreamConstants.END_ELEMENT) return result;\n if (event != XMLStreamConstants.START_ELEMENT) continue;");
|
|
188
188
|
for (i, b) in u.branches.iter().enumerate() {
|
|
189
189
|
let expr = self.read_value(&b.type_ref, ir);
|
|
190
|
-
let variant =
|
|
190
|
+
let variant = union_variant_name(b);
|
|
191
191
|
let _=writeln!(out," {}if (reader.getLocalName().equals({:?})) result = new {name}.{variant}({expr});",if i==0 {""}else{"else "},b.xml_name);
|
|
192
192
|
}
|
|
193
193
|
out.push_str(" else skip(reader);\n }\n return result;\n");
|
|
@@ -228,7 +228,7 @@ impl JavaCodegen {
|
|
|
228
228
|
}
|
|
229
229
|
TypeDef::Union(u) => {
|
|
230
230
|
for (i, b) in u.branches.iter().enumerate() {
|
|
231
|
-
let variant =
|
|
231
|
+
let variant = union_variant_name(b);
|
|
232
232
|
let _ = writeln!(
|
|
233
233
|
out,
|
|
234
234
|
" {}if (value instanceof {name}.{variant} branch) {{",
|
|
@@ -242,6 +242,8 @@ impl JavaCodegen {
|
|
|
242
242
|
if u.is_lexical() {
|
|
243
243
|
let text = self.format_scalar(&b.type_ref, access, ir);
|
|
244
244
|
let _ = writeln!(out, " writer.writeCharacters({text});");
|
|
245
|
+
} else if u.is_mixed_content() && b.xml_name == "#text" {
|
|
246
|
+
let _ = writeln!(out, " writer.writeCharacters({access});");
|
|
245
247
|
} else {
|
|
246
248
|
self.emit_write_value(
|
|
247
249
|
&mut out,
|
|
@@ -296,6 +298,26 @@ impl JavaCodegen {
|
|
|
296
298
|
if text_field.is_some() {
|
|
297
299
|
out.push_str(" if (event == XMLStreamConstants.CHARACTERS || event == XMLStreamConstants.CDATA) text.append(reader.getText());\n");
|
|
298
300
|
}
|
|
301
|
+
if s.is_mixed {
|
|
302
|
+
if let Some((field, id, union)) = fields.iter().find_map(|(field, id)| {
|
|
303
|
+
self.resolve_union_def(&field.type_ref, ir)
|
|
304
|
+
.filter(|union| union.is_mixed_content())
|
|
305
|
+
.map(|union| (field, id, union))
|
|
306
|
+
}) {
|
|
307
|
+
let union_name = type_ident(&union.qname);
|
|
308
|
+
let text_branch = union
|
|
309
|
+
.branches
|
|
310
|
+
.iter()
|
|
311
|
+
.find(|branch| branch.xml_name == "#text");
|
|
312
|
+
if let Some(text_branch) = text_branch {
|
|
313
|
+
let variant = union_variant_name(text_branch);
|
|
314
|
+
let val = format!("new {union_name}.{variant}(reader.getText())");
|
|
315
|
+
out.push_str(" if (event == XMLStreamConstants.CHARACTERS || event == XMLStreamConstants.CDATA || event == XMLStreamConstants.SPACE) {\n");
|
|
316
|
+
self.assign_value(out, field, id, &val, " ");
|
|
317
|
+
out.push_str(" continue;\n }\n");
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
299
321
|
out.push_str(" if (event != XMLStreamConstants.START_ELEMENT) continue;\n");
|
|
300
322
|
let mut first = true;
|
|
301
323
|
for (f, id) in &fields {
|
|
@@ -306,6 +328,9 @@ impl JavaCodegen {
|
|
|
306
328
|
if let Some(u) = self.resolve_union_def(&f.type_ref, ir) {
|
|
307
329
|
let union_name = type_ident(&u.qname);
|
|
308
330
|
for branch in &u.branches {
|
|
331
|
+
if branch.xml_name == "#text" {
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
309
334
|
let _ = writeln!(
|
|
310
335
|
out,
|
|
311
336
|
" {}if (reader.getLocalName().equals({:?})) {{",
|
|
@@ -314,7 +339,7 @@ impl JavaCodegen {
|
|
|
314
339
|
);
|
|
315
340
|
first = false;
|
|
316
341
|
let expr = self.read_value(&branch.type_ref, ir);
|
|
317
|
-
let variant =
|
|
342
|
+
let variant = union_variant_name(branch);
|
|
318
343
|
let union_val = format!("new {union_name}.{variant}({expr})");
|
|
319
344
|
self.assign_value(out, f, id, &union_val, " ");
|
|
320
345
|
out.push_str(" }\n");
|
|
@@ -13,7 +13,7 @@ use crate::codegen::{
|
|
|
13
13
|
};
|
|
14
14
|
use crate::ir::{
|
|
15
15
|
EnumDef, PrimitiveType, QName, RestrictionFacets, SchemaIR, SimpleTypeDef, StructDef, TypeDef,
|
|
16
|
-
TypeRef, UnionDef,
|
|
16
|
+
TypeRef, UnionBranch, UnionDef,
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
/// Target backend for Java code emission.
|
|
@@ -218,6 +218,15 @@ pub(super) fn type_ident(q: &QName) -> String {
|
|
|
218
218
|
lookup_type_name(q, || to_java_type_name(&q.local))
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
fn union_variant_name(branch: &UnionBranch) -> String {
|
|
222
|
+
let name = to_java_type_name(&branch.variant_name);
|
|
223
|
+
if matches!(&branch.type_ref, TypeRef::Named(qname) if type_ident(qname) == name) {
|
|
224
|
+
format!("{name}Branch")
|
|
225
|
+
} else {
|
|
226
|
+
name
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
221
230
|
/// Convert an XML enumeration variant name to SCREAMING_SNAKE_CASE for Java enum constants.
|
|
222
231
|
pub fn to_java_enum_constant(name: &str) -> String {
|
|
223
232
|
let normalized = normalize_symbol_name(name);
|
|
@@ -632,11 +641,7 @@ impl JavaCodegen {
|
|
|
632
641
|
"public static "
|
|
633
642
|
};
|
|
634
643
|
|
|
635
|
-
let variant_names: Vec<String> = u
|
|
636
|
-
.branches
|
|
637
|
-
.iter()
|
|
638
|
-
.map(|b| to_java_type_name(&b.variant_name))
|
|
639
|
-
.collect();
|
|
644
|
+
let variant_names: Vec<String> = u.branches.iter().map(union_variant_name).collect();
|
|
640
645
|
|
|
641
646
|
let permits_clause = variant_names
|
|
642
647
|
.iter()
|
|
@@ -656,7 +661,7 @@ impl JavaCodegen {
|
|
|
656
661
|
.branches
|
|
657
662
|
.iter()
|
|
658
663
|
.map(|b| {
|
|
659
|
-
let vn =
|
|
664
|
+
let vn = union_variant_name(b);
|
|
660
665
|
format!(
|
|
661
666
|
"@JsonSubTypes.Type(value = {}.{}.class, name = {:?})",
|
|
662
667
|
java_name, vn, b.xml_name
|
|
@@ -674,7 +679,7 @@ impl JavaCodegen {
|
|
|
674
679
|
);
|
|
675
680
|
|
|
676
681
|
for branch in &u.branches {
|
|
677
|
-
let variant_name =
|
|
682
|
+
let variant_name = union_variant_name(branch);
|
|
678
683
|
let branch_type = self.context.map_type_ref(&branch.type_ref);
|
|
679
684
|
|
|
680
685
|
if let Some(ref doc) = branch.documentation {
|
|
@@ -519,6 +519,32 @@ impl PythonCodegen {
|
|
|
519
519
|
fn emit_union(&self, out: &mut String, u: &UnionDef) {
|
|
520
520
|
let union_name = type_ident(&u.qname);
|
|
521
521
|
|
|
522
|
+
if u.is_mixed_content() {
|
|
523
|
+
let value_types = u
|
|
524
|
+
.branches
|
|
525
|
+
.iter()
|
|
526
|
+
.map(|branch| self.context.map_type_ref(&branch.type_ref))
|
|
527
|
+
.collect::<Vec<_>>();
|
|
528
|
+
let value_type = value_types
|
|
529
|
+
.into_iter()
|
|
530
|
+
.collect::<std::collections::BTreeSet<_>>()
|
|
531
|
+
.into_iter()
|
|
532
|
+
.collect::<Vec<_>>()
|
|
533
|
+
.join(" | ");
|
|
534
|
+
if self.options.backend == PythonBackend::Pydantic {
|
|
535
|
+
let _ = writeln!(out, "class {}(BaseModel):", union_name);
|
|
536
|
+
} else {
|
|
537
|
+
let _ = writeln!(
|
|
538
|
+
out,
|
|
539
|
+
"@dataclass(slots=True, kw_only=True)\nclass {}:",
|
|
540
|
+
union_name
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
out.push_str(" kind: str\n");
|
|
544
|
+
let _ = writeln!(out, " value: {}", value_type);
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
|
|
522
548
|
if let Some(ref doc) = u.documentation {
|
|
523
549
|
let _ = writeln!(out, "# {}", doc.trim());
|
|
524
550
|
}
|
|
@@ -595,7 +621,7 @@ impl PythonCodegen {
|
|
|
595
621
|
has_body = true;
|
|
596
622
|
}
|
|
597
623
|
|
|
598
|
-
if self.options.emit_meta {
|
|
624
|
+
if self.options.emit_meta || s.is_mixed {
|
|
599
625
|
if has_body {
|
|
600
626
|
out.push('\n');
|
|
601
627
|
}
|
|
@@ -610,6 +636,38 @@ impl PythonCodegen {
|
|
|
610
636
|
if s.is_abstract {
|
|
611
637
|
out.push_str(" abstract = True\n");
|
|
612
638
|
}
|
|
639
|
+
if s.is_mixed {
|
|
640
|
+
if let Some(item_field) = s
|
|
641
|
+
.fields
|
|
642
|
+
.iter()
|
|
643
|
+
.find(|field| field.xml_name.is_empty() && field.kind == FieldKind::Element)
|
|
644
|
+
{
|
|
645
|
+
let _ = writeln!(out, " mixed_items = {:?}", item_field.name);
|
|
646
|
+
}
|
|
647
|
+
if let Some(union) = s.fields.iter().find_map(|field| match &field.type_ref {
|
|
648
|
+
TypeRef::Named(qname) => match ir.types.get(qname) {
|
|
649
|
+
Some(TypeDef::Union(union)) if union.is_mixed_content() => Some(union),
|
|
650
|
+
_ => None,
|
|
651
|
+
},
|
|
652
|
+
_ => None,
|
|
653
|
+
}) {
|
|
654
|
+
out.push_str(" mixed_branches = (\n");
|
|
655
|
+
for branch in &union.branches {
|
|
656
|
+
let branch_type = self.context.map_type_ref(&branch.type_ref);
|
|
657
|
+
let namespace = branch
|
|
658
|
+
.namespace
|
|
659
|
+
.as_ref()
|
|
660
|
+
.map(|ns| format!("{ns:?}"))
|
|
661
|
+
.unwrap_or_else(|| "None".into());
|
|
662
|
+
let _ = writeln!(
|
|
663
|
+
out,
|
|
664
|
+
" ({:?}, {:?}, {}, {:?}),",
|
|
665
|
+
branch.variant_name, branch.xml_name, namespace, branch_type
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
out.push_str(" )\n");
|
|
669
|
+
}
|
|
670
|
+
}
|
|
613
671
|
has_body = true;
|
|
614
672
|
}
|
|
615
673
|
|