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