polyxml 0.3.0__tar.gz → 0.4.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.3.0 → polyxml-0.4.0}/Cargo.lock +87 -58
- {polyxml-0.3.0 → polyxml-0.4.0}/Cargo.toml +1 -1
- {polyxml-0.3.0 → polyxml-0.4.0}/PKG-INFO +1 -1
- {polyxml-0.3.0 → polyxml-0.4.0}/crates/polyxml-core/Cargo.toml +2 -2
- {polyxml-0.3.0 → polyxml-0.4.0}/crates/polyxml-core/src/error.rs +4 -1
- {polyxml-0.3.0 → polyxml-0.4.0}/crates/polyxml-core/src/parser.rs +58 -16
- {polyxml-0.3.0 → polyxml-0.4.0}/crates/polyxml-python/Cargo.toml +1 -1
- {polyxml-0.3.0 → polyxml-0.4.0}/crates/polyxml-python/src/lib.rs +65 -35
- {polyxml-0.3.0 → polyxml-0.4.0}/crates/polyxml-python/tests/test_polyxml.py +17 -0
- {polyxml-0.3.0 → polyxml-0.4.0}/pyproject.toml +1 -1
- {polyxml-0.3.0 → polyxml-0.4.0}/README.md +0 -0
- {polyxml-0.3.0 → polyxml-0.4.0}/crates/polyxml-core/README.md +0 -0
- {polyxml-0.3.0 → polyxml-0.4.0}/crates/polyxml-core/benches/core_benchmarks.rs +0 -0
- {polyxml-0.3.0 → polyxml-0.4.0}/crates/polyxml-core/src/converters.rs +0 -0
- {polyxml-0.3.0 → polyxml-0.4.0}/crates/polyxml-core/src/lib.rs +0 -0
- {polyxml-0.3.0 → polyxml-0.4.0}/crates/polyxml-core/src/schema.rs +0 -0
- {polyxml-0.3.0 → polyxml-0.4.0}/crates/polyxml-core/src/serializer.rs +0 -0
- {polyxml-0.3.0 → polyxml-0.4.0}/crates/polyxml-core/src/value.rs +0 -0
- {polyxml-0.3.0 → polyxml-0.4.0}/crates/polyxml-core/tests/test_core.rs +0 -0
- {polyxml-0.3.0 → polyxml-0.4.0}/crates/polyxml-core/tests/test_security.rs +0 -0
- {polyxml-0.3.0 → polyxml-0.4.0}/crates/polyxml-python/README.md +0 -0
- {polyxml-0.3.0 → polyxml-0.4.0}/python/polyxml/__init__.py +0 -0
- {polyxml-0.3.0 → polyxml-0.4.0}/python/polyxml/py.typed +0 -0
|
@@ -11,6 +11,15 @@ dependencies = [
|
|
|
11
11
|
"memchr",
|
|
12
12
|
]
|
|
13
13
|
|
|
14
|
+
[[package]]
|
|
15
|
+
name = "alloca"
|
|
16
|
+
version = "0.4.0"
|
|
17
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
18
|
+
checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4"
|
|
19
|
+
dependencies = [
|
|
20
|
+
"cc",
|
|
21
|
+
]
|
|
22
|
+
|
|
14
23
|
[[package]]
|
|
15
24
|
name = "anes"
|
|
16
25
|
version = "0.1.6"
|
|
@@ -41,6 +50,16 @@ version = "0.3.0"
|
|
|
41
50
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
42
51
|
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
|
43
52
|
|
|
53
|
+
[[package]]
|
|
54
|
+
name = "cc"
|
|
55
|
+
version = "1.4.5"
|
|
56
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
57
|
+
checksum = "005ec2760ca554fae18df7a11195552ec576cd665632a881bc011d5bb2fd4d80"
|
|
58
|
+
dependencies = [
|
|
59
|
+
"find-msvc-tools",
|
|
60
|
+
"shlex",
|
|
61
|
+
]
|
|
62
|
+
|
|
44
63
|
[[package]]
|
|
45
64
|
name = "cfg-if"
|
|
46
65
|
version = "1.0.4"
|
|
@@ -110,10 +129,11 @@ dependencies = [
|
|
|
110
129
|
|
|
111
130
|
[[package]]
|
|
112
131
|
name = "criterion"
|
|
113
|
-
version = "0.
|
|
132
|
+
version = "0.8.2"
|
|
114
133
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
115
|
-
checksum = "
|
|
134
|
+
checksum = "950046b2aa2492f9a536f5f4f9a3de7b9e2476e575e05bd6c333371add4d98f3"
|
|
116
135
|
dependencies = [
|
|
136
|
+
"alloca",
|
|
117
137
|
"anes",
|
|
118
138
|
"cast",
|
|
119
139
|
"ciborium",
|
|
@@ -122,6 +142,7 @@ dependencies = [
|
|
|
122
142
|
"itertools",
|
|
123
143
|
"num-traits",
|
|
124
144
|
"oorandom",
|
|
145
|
+
"page_size",
|
|
125
146
|
"regex",
|
|
126
147
|
"serde",
|
|
127
148
|
"serde_json",
|
|
@@ -131,9 +152,9 @@ dependencies = [
|
|
|
131
152
|
|
|
132
153
|
[[package]]
|
|
133
154
|
name = "criterion-plot"
|
|
134
|
-
version = "0.
|
|
155
|
+
version = "0.8.2"
|
|
135
156
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
136
|
-
checksum = "
|
|
157
|
+
checksum = "d8d80a2f4f5b554395e47b5d8305bc3d27813bacb73493eb1001e8f76dae29ea"
|
|
137
158
|
dependencies = [
|
|
138
159
|
"cast",
|
|
139
160
|
"itertools",
|
|
@@ -167,6 +188,12 @@ version = "1.18.0"
|
|
|
167
188
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
168
189
|
checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34"
|
|
169
190
|
|
|
191
|
+
[[package]]
|
|
192
|
+
name = "find-msvc-tools"
|
|
193
|
+
version = "0.1.12"
|
|
194
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
195
|
+
checksum = "3e0f1c7c3a72c66fd80abe965175f7523475c0489a87d3ff9d6e8c87d87a9d2d"
|
|
196
|
+
|
|
170
197
|
[[package]]
|
|
171
198
|
name = "half"
|
|
172
199
|
version = "2.7.1"
|
|
@@ -184,15 +211,6 @@ version = "0.5.0"
|
|
|
184
211
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
185
212
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|
186
213
|
|
|
187
|
-
[[package]]
|
|
188
|
-
name = "indoc"
|
|
189
|
-
version = "2.0.7"
|
|
190
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
191
|
-
checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
|
|
192
|
-
dependencies = [
|
|
193
|
-
"rustversion",
|
|
194
|
-
]
|
|
195
|
-
|
|
196
214
|
[[package]]
|
|
197
215
|
name = "itertools"
|
|
198
216
|
version = "0.13.0"
|
|
@@ -287,15 +305,6 @@ version = "2.8.3"
|
|
|
287
305
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
288
306
|
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
|
289
307
|
|
|
290
|
-
[[package]]
|
|
291
|
-
name = "memoffset"
|
|
292
|
-
version = "0.9.1"
|
|
293
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
294
|
-
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
|
295
|
-
dependencies = [
|
|
296
|
-
"autocfg",
|
|
297
|
-
]
|
|
298
|
-
|
|
299
308
|
[[package]]
|
|
300
309
|
name = "napi"
|
|
301
310
|
version = "2.16.17"
|
|
@@ -374,9 +383,19 @@ version = "11.1.5"
|
|
|
374
383
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
375
384
|
checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
|
|
376
385
|
|
|
386
|
+
[[package]]
|
|
387
|
+
name = "page_size"
|
|
388
|
+
version = "0.6.0"
|
|
389
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
390
|
+
checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da"
|
|
391
|
+
dependencies = [
|
|
392
|
+
"libc",
|
|
393
|
+
"winapi",
|
|
394
|
+
]
|
|
395
|
+
|
|
377
396
|
[[package]]
|
|
378
397
|
name = "polyxml"
|
|
379
|
-
version = "0.
|
|
398
|
+
version = "0.4.0"
|
|
380
399
|
dependencies = [
|
|
381
400
|
"criterion",
|
|
382
401
|
"lexical-core",
|
|
@@ -389,14 +408,14 @@ dependencies = [
|
|
|
389
408
|
|
|
390
409
|
[[package]]
|
|
391
410
|
name = "polyxml-c"
|
|
392
|
-
version = "0.
|
|
411
|
+
version = "0.4.0"
|
|
393
412
|
dependencies = [
|
|
394
413
|
"polyxml",
|
|
395
414
|
]
|
|
396
415
|
|
|
397
416
|
[[package]]
|
|
398
417
|
name = "polyxml-js"
|
|
399
|
-
version = "0.
|
|
418
|
+
version = "0.4.0"
|
|
400
419
|
dependencies = [
|
|
401
420
|
"napi",
|
|
402
421
|
"napi-build",
|
|
@@ -406,7 +425,7 @@ dependencies = [
|
|
|
406
425
|
|
|
407
426
|
[[package]]
|
|
408
427
|
name = "polyxml-python"
|
|
409
|
-
version = "0.
|
|
428
|
+
version = "0.4.0"
|
|
410
429
|
dependencies = [
|
|
411
430
|
"lexical-core",
|
|
412
431
|
"polyxml",
|
|
@@ -441,37 +460,32 @@ dependencies = [
|
|
|
441
460
|
|
|
442
461
|
[[package]]
|
|
443
462
|
name = "pyo3"
|
|
444
|
-
version = "0.
|
|
463
|
+
version = "0.29.2"
|
|
445
464
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
446
|
-
checksum = "
|
|
465
|
+
checksum = "4688ddedf473e32662b9b067670129a8afb8c18e351482c70d62ba4a88171e8b"
|
|
447
466
|
dependencies = [
|
|
448
|
-
"cfg-if",
|
|
449
|
-
"indoc",
|
|
450
467
|
"libc",
|
|
451
|
-
"memoffset",
|
|
452
468
|
"once_cell",
|
|
453
469
|
"portable-atomic",
|
|
454
470
|
"pyo3-build-config",
|
|
455
471
|
"pyo3-ffi",
|
|
456
472
|
"pyo3-macros",
|
|
457
|
-
"unindent",
|
|
458
473
|
]
|
|
459
474
|
|
|
460
475
|
[[package]]
|
|
461
476
|
name = "pyo3-build-config"
|
|
462
|
-
version = "0.
|
|
477
|
+
version = "0.29.2"
|
|
463
478
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
464
|
-
checksum = "
|
|
479
|
+
checksum = "f41027e41b4bd03f6e60f9f417fe24a6341a6bb744edd62b6f709f2a52ea30e9"
|
|
465
480
|
dependencies = [
|
|
466
|
-
"once_cell",
|
|
467
481
|
"target-lexicon",
|
|
468
482
|
]
|
|
469
483
|
|
|
470
484
|
[[package]]
|
|
471
485
|
name = "pyo3-ffi"
|
|
472
|
-
version = "0.
|
|
486
|
+
version = "0.29.2"
|
|
473
487
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
474
|
-
checksum = "
|
|
488
|
+
checksum = "e591a95526fead067432c3b3a33fc74770b87b1e04e73671090d9c2055a2b327"
|
|
475
489
|
dependencies = [
|
|
476
490
|
"libc",
|
|
477
491
|
"pyo3-build-config",
|
|
@@ -479,9 +493,9 @@ dependencies = [
|
|
|
479
493
|
|
|
480
494
|
[[package]]
|
|
481
495
|
name = "pyo3-macros"
|
|
482
|
-
version = "0.
|
|
496
|
+
version = "0.29.2"
|
|
483
497
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
484
|
-
checksum = "
|
|
498
|
+
checksum = "73225868fc1cd84eef2c3c230ddb91273bf1de46aeb8a4248da76d32a0924a1c"
|
|
485
499
|
dependencies = [
|
|
486
500
|
"proc-macro2",
|
|
487
501
|
"pyo3-macros-backend",
|
|
@@ -491,22 +505,21 @@ dependencies = [
|
|
|
491
505
|
|
|
492
506
|
[[package]]
|
|
493
507
|
name = "pyo3-macros-backend"
|
|
494
|
-
version = "0.
|
|
508
|
+
version = "0.29.2"
|
|
495
509
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
496
|
-
checksum = "
|
|
510
|
+
checksum = "571575aa3749fa6216757dd47d2a3e7ef360f329a40f0666a9fbd14889024952"
|
|
497
511
|
dependencies = [
|
|
498
512
|
"heck",
|
|
499
513
|
"proc-macro2",
|
|
500
|
-
"pyo3-build-config",
|
|
501
514
|
"quote",
|
|
502
515
|
"syn 2.0.119",
|
|
503
516
|
]
|
|
504
517
|
|
|
505
518
|
[[package]]
|
|
506
519
|
name = "quick-xml"
|
|
507
|
-
version = "0.
|
|
520
|
+
version = "0.42.0"
|
|
508
521
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
509
|
-
checksum = "
|
|
522
|
+
checksum = "41b1177fdf999d2321d3fb46ff47159d9c1fb9ad66a4879f8c50a0b504615e9b"
|
|
510
523
|
dependencies = [
|
|
511
524
|
"memchr",
|
|
512
525
|
"serde",
|
|
@@ -550,12 +563,6 @@ version = "0.8.11"
|
|
|
550
563
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
551
564
|
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
|
552
565
|
|
|
553
|
-
[[package]]
|
|
554
|
-
name = "rustversion"
|
|
555
|
-
version = "1.0.23"
|
|
556
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
557
|
-
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
|
|
558
|
-
|
|
559
566
|
[[package]]
|
|
560
567
|
name = "same-file"
|
|
561
568
|
version = "1.0.6"
|
|
@@ -614,6 +621,12 @@ dependencies = [
|
|
|
614
621
|
"zmij",
|
|
615
622
|
]
|
|
616
623
|
|
|
624
|
+
[[package]]
|
|
625
|
+
name = "shlex"
|
|
626
|
+
version = "2.0.1"
|
|
627
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
628
|
+
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
|
629
|
+
|
|
617
630
|
[[package]]
|
|
618
631
|
name = "smallvec"
|
|
619
632
|
version = "1.16.0"
|
|
@@ -644,9 +657,9 @@ dependencies = [
|
|
|
644
657
|
|
|
645
658
|
[[package]]
|
|
646
659
|
name = "target-lexicon"
|
|
647
|
-
version = "0.
|
|
660
|
+
version = "0.13.5"
|
|
648
661
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
649
|
-
checksum = "
|
|
662
|
+
checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
|
650
663
|
|
|
651
664
|
[[package]]
|
|
652
665
|
name = "thiserror"
|
|
@@ -690,12 +703,6 @@ version = "1.13.3"
|
|
|
690
703
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
691
704
|
checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
|
|
692
705
|
|
|
693
|
-
[[package]]
|
|
694
|
-
name = "unindent"
|
|
695
|
-
version = "0.2.4"
|
|
696
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
697
|
-
checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
|
|
698
|
-
|
|
699
706
|
[[package]]
|
|
700
707
|
name = "walkdir"
|
|
701
708
|
version = "2.5.0"
|
|
@@ -706,6 +713,22 @@ dependencies = [
|
|
|
706
713
|
"winapi-util",
|
|
707
714
|
]
|
|
708
715
|
|
|
716
|
+
[[package]]
|
|
717
|
+
name = "winapi"
|
|
718
|
+
version = "0.3.9"
|
|
719
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
720
|
+
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
|
721
|
+
dependencies = [
|
|
722
|
+
"winapi-i686-pc-windows-gnu",
|
|
723
|
+
"winapi-x86_64-pc-windows-gnu",
|
|
724
|
+
]
|
|
725
|
+
|
|
726
|
+
[[package]]
|
|
727
|
+
name = "winapi-i686-pc-windows-gnu"
|
|
728
|
+
version = "0.4.0"
|
|
729
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
730
|
+
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
|
731
|
+
|
|
709
732
|
[[package]]
|
|
710
733
|
name = "winapi-util"
|
|
711
734
|
version = "0.1.11"
|
|
@@ -715,6 +738,12 @@ dependencies = [
|
|
|
715
738
|
"windows-sys",
|
|
716
739
|
]
|
|
717
740
|
|
|
741
|
+
[[package]]
|
|
742
|
+
name = "winapi-x86_64-pc-windows-gnu"
|
|
743
|
+
version = "0.4.0"
|
|
744
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
745
|
+
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|
746
|
+
|
|
718
747
|
[[package]]
|
|
719
748
|
name = "windows-link"
|
|
720
749
|
version = "0.2.1"
|
|
@@ -11,14 +11,14 @@ keywords = ["xml", "deserializer", "serializer", "data-binding", "quick-xml"]
|
|
|
11
11
|
categories = ["parsing", "encoding"]
|
|
12
12
|
|
|
13
13
|
[dependencies]
|
|
14
|
-
quick-xml = { version = "0.
|
|
14
|
+
quick-xml = { version = "0.42", features = ["serialize"] }
|
|
15
15
|
lexical-core = "1.0"
|
|
16
16
|
memchr = "2.8"
|
|
17
17
|
thiserror = "2.0"
|
|
18
18
|
smallvec = "1.16"
|
|
19
19
|
|
|
20
20
|
[dev-dependencies]
|
|
21
|
-
criterion = { version = "0.
|
|
21
|
+
criterion = { version = "0.8", default-features = false, features = ["cargo_bench_support"] }
|
|
22
22
|
pretty_assertions = "1.4"
|
|
23
23
|
|
|
24
24
|
[[bench]]
|
|
@@ -31,8 +31,11 @@ pub enum PolyXmlError {
|
|
|
31
31
|
#[error("Maximum XML recursion depth exceeded: current {current} >= limit {max_depth}")]
|
|
32
32
|
MaxDepthExceeded { max_depth: usize, current: usize },
|
|
33
33
|
|
|
34
|
-
#[error("XML parsing
|
|
34
|
+
#[error("XML parsing error: {0}")]
|
|
35
35
|
XmlError(#[from] quick_xml::Error),
|
|
36
|
+
|
|
37
|
+
#[error("XML unescape error: {0}")]
|
|
38
|
+
EscapeError(#[from] quick_xml::escape::EscapeError),
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
pub type Result<T> = std::result::Result<T, PolyXmlError>;
|
|
@@ -86,8 +86,8 @@ pub struct XmlDeserializer;
|
|
|
86
86
|
fn is_nil_element(e: &BytesStart) -> bool {
|
|
87
87
|
for attr in e.attributes().flatten() {
|
|
88
88
|
let key = attr.key.local_name();
|
|
89
|
-
if (key.as_ref() ==
|
|
90
|
-
&& (attr.value.as_ref() ==
|
|
89
|
+
if (key.as_ref() == "nil" || key.as_ref() == "xsi:nil")
|
|
90
|
+
&& (attr.value.as_ref() == "true" || attr.value.as_ref() == "1")
|
|
91
91
|
{
|
|
92
92
|
return true;
|
|
93
93
|
}
|
|
@@ -95,6 +95,33 @@ fn is_nil_element(e: &BytesStart) -> bool {
|
|
|
95
95
|
false
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
fn append_general_ref(
|
|
99
|
+
e: &quick_xml::events::BytesRef,
|
|
100
|
+
active_scalar: bool,
|
|
101
|
+
text_buf: &mut Vec<u8>,
|
|
102
|
+
frame_text_buf: Option<&mut Vec<u8>>,
|
|
103
|
+
) -> Result<()> {
|
|
104
|
+
let mut char_buf = [0u8; 4];
|
|
105
|
+
let resolved_bytes: &[u8] = if e.is_char_ref() {
|
|
106
|
+
if let Some(ch) = e.resolve_char_ref().map_err(PolyXmlError::XmlError)? {
|
|
107
|
+
ch.encode_utf8(&mut char_buf).as_bytes()
|
|
108
|
+
} else {
|
|
109
|
+
b""
|
|
110
|
+
}
|
|
111
|
+
} else if let Some(s) = quick_xml::escape::resolve_xml_entity(e.as_ref()) {
|
|
112
|
+
s.as_bytes()
|
|
113
|
+
} else {
|
|
114
|
+
e.as_ref().as_bytes()
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
if active_scalar {
|
|
118
|
+
text_buf.extend_from_slice(resolved_bytes);
|
|
119
|
+
} else if let Some(tb) = frame_text_buf {
|
|
120
|
+
tb.extend_from_slice(resolved_bytes);
|
|
121
|
+
}
|
|
122
|
+
Ok(())
|
|
123
|
+
}
|
|
124
|
+
|
|
98
125
|
pub const DEFAULT_MAX_DEPTH: usize = 256;
|
|
99
126
|
|
|
100
127
|
impl XmlDeserializer {
|
|
@@ -108,7 +135,6 @@ impl XmlDeserializer {
|
|
|
108
135
|
max_depth: usize,
|
|
109
136
|
) -> Result<PolyValue> {
|
|
110
137
|
let mut reader = Reader::from_reader(xml_bytes);
|
|
111
|
-
reader.config_mut().trim_text(true);
|
|
112
138
|
let mut buf = Vec::new();
|
|
113
139
|
|
|
114
140
|
loop {
|
|
@@ -166,7 +192,10 @@ impl XmlDeserializer {
|
|
|
166
192
|
let is_nil = is_nil_element(e);
|
|
167
193
|
|
|
168
194
|
let current_schema = Arc::clone(&stack.last().unwrap().schema);
|
|
169
|
-
if let Some(&field_idx) = current_schema
|
|
195
|
+
if let Some(&field_idx) = current_schema
|
|
196
|
+
.element_map
|
|
197
|
+
.get(local_name.as_ref().as_bytes())
|
|
198
|
+
{
|
|
170
199
|
let field = ¤t_schema.fields[field_idx];
|
|
171
200
|
match &field.val_type {
|
|
172
201
|
ValueType::Scalar(st) => {
|
|
@@ -210,7 +239,10 @@ impl XmlDeserializer {
|
|
|
210
239
|
let is_nil = is_nil_element(e);
|
|
211
240
|
|
|
212
241
|
let current_schema = Arc::clone(&stack.last().unwrap().schema);
|
|
213
|
-
if let Some(&field_idx) = current_schema
|
|
242
|
+
if let Some(&field_idx) = current_schema
|
|
243
|
+
.element_map
|
|
244
|
+
.get(local_name.as_ref().as_bytes())
|
|
245
|
+
{
|
|
214
246
|
let field = ¤t_schema.fields[field_idx];
|
|
215
247
|
match &field.val_type {
|
|
216
248
|
ValueType::Scalar(_) => {
|
|
@@ -251,7 +283,7 @@ impl XmlDeserializer {
|
|
|
251
283
|
}
|
|
252
284
|
}
|
|
253
285
|
Ok(Event::Text(ref e)) => {
|
|
254
|
-
let unescaped =
|
|
286
|
+
let unescaped = quick_xml::escape::unescape(e.as_ref())?;
|
|
255
287
|
if active_scalar_field.is_some() {
|
|
256
288
|
text_buf.extend_from_slice(unescaped.as_bytes());
|
|
257
289
|
} else if let Some(frame) = stack.last_mut() {
|
|
@@ -262,13 +294,17 @@ impl XmlDeserializer {
|
|
|
262
294
|
}
|
|
263
295
|
Ok(Event::CData(ref e)) => {
|
|
264
296
|
if active_scalar_field.is_some() {
|
|
265
|
-
text_buf.extend_from_slice(e.as_ref());
|
|
297
|
+
text_buf.extend_from_slice(e.as_ref().as_bytes());
|
|
266
298
|
} else if let Some(frame) = stack.last_mut() {
|
|
267
299
|
if let Some(ref mut tb) = frame.frame_text_buf {
|
|
268
|
-
tb.extend_from_slice(e.as_ref());
|
|
300
|
+
tb.extend_from_slice(e.as_ref().as_bytes());
|
|
269
301
|
}
|
|
270
302
|
}
|
|
271
303
|
}
|
|
304
|
+
Ok(Event::GeneralRef(ref e)) => {
|
|
305
|
+
let frame_tb = stack.last_mut().and_then(|f| f.frame_text_buf.as_mut());
|
|
306
|
+
append_general_ref(e, active_scalar_field.is_some(), &mut text_buf, frame_tb)?;
|
|
307
|
+
}
|
|
272
308
|
Ok(Event::End(ref e)) => {
|
|
273
309
|
if let Some((field_idx, ref scalar_type, is_list)) = active_scalar_field.take()
|
|
274
310
|
{
|
|
@@ -287,7 +323,10 @@ impl XmlDeserializer {
|
|
|
287
323
|
let instance = finished_frame.finish()?;
|
|
288
324
|
let parent = stack.last_mut().unwrap();
|
|
289
325
|
|
|
290
|
-
if let Some(&field_idx) = parent
|
|
326
|
+
if let Some(&field_idx) = parent
|
|
327
|
+
.schema
|
|
328
|
+
.element_map
|
|
329
|
+
.get(local_name.as_ref().as_bytes())
|
|
291
330
|
{
|
|
292
331
|
let field = &parent.schema.fields[field_idx];
|
|
293
332
|
if matches!(field.val_type, ValueType::List(_)) {
|
|
@@ -321,11 +360,15 @@ impl XmlDeserializer {
|
|
|
321
360
|
pub(crate) fn parse_attributes(e: &BytesStart, frame: &mut StackFrame) -> Result<()> {
|
|
322
361
|
for attr in e.attributes().flatten() {
|
|
323
362
|
let key = attr.key.local_name();
|
|
324
|
-
if let Some(&field_idx) = frame.schema.attribute_map.get(key.as_ref()) {
|
|
363
|
+
if let Some(&field_idx) = frame.schema.attribute_map.get(key.as_ref().as_bytes()) {
|
|
325
364
|
let field = &frame.schema.fields[field_idx];
|
|
326
365
|
if let ValueType::Scalar(ref st) = field.val_type {
|
|
327
|
-
let unescaped = attr.
|
|
328
|
-
let val = ValueConverter::parse_scalar(
|
|
366
|
+
let unescaped = quick_xml::escape::unescape(attr.value.as_ref())?;
|
|
367
|
+
let val = ValueConverter::parse_scalar(
|
|
368
|
+
st,
|
|
369
|
+
unescaped.as_ref().as_bytes(),
|
|
370
|
+
&field.name,
|
|
371
|
+
)?;
|
|
329
372
|
frame.values[field_idx] = Some(val);
|
|
330
373
|
}
|
|
331
374
|
}
|
|
@@ -344,8 +387,7 @@ pub struct XmlItemStream<R: std::io::BufRead> {
|
|
|
344
387
|
|
|
345
388
|
impl<R: std::io::BufRead> XmlItemStream<R> {
|
|
346
389
|
pub fn new(reader: R, schema: Arc<ModelSchema>, target_tag: &[u8]) -> Self {
|
|
347
|
-
let
|
|
348
|
-
xml_reader.config_mut().trim_text(true);
|
|
390
|
+
let xml_reader = Reader::from_reader(reader);
|
|
349
391
|
Self {
|
|
350
392
|
reader: xml_reader,
|
|
351
393
|
schema,
|
|
@@ -361,7 +403,7 @@ impl<R: std::io::BufRead> XmlItemStream<R> {
|
|
|
361
403
|
match self.reader.read_event_into(&mut self.buf) {
|
|
362
404
|
Ok(Event::Start(ref e)) => {
|
|
363
405
|
let local = e.local_name();
|
|
364
|
-
if local.as_ref() == self.target_tag.as_slice() {
|
|
406
|
+
if local.as_ref().as_bytes() == self.target_tag.as_slice() {
|
|
365
407
|
let item = XmlDeserializer::parse_sub_tree(
|
|
366
408
|
&mut self.reader,
|
|
367
409
|
Arc::clone(&self.schema),
|
|
@@ -373,7 +415,7 @@ impl<R: std::io::BufRead> XmlItemStream<R> {
|
|
|
373
415
|
}
|
|
374
416
|
Ok(Event::Empty(ref e)) => {
|
|
375
417
|
let local = e.local_name();
|
|
376
|
-
if local.as_ref() == self.target_tag.as_slice() {
|
|
418
|
+
if local.as_ref().as_bytes() == self.target_tag.as_slice() {
|
|
377
419
|
let mut frame = StackFrame::new(Arc::clone(&self.schema));
|
|
378
420
|
XmlDeserializer::parse_attributes(e, &mut frame)?;
|
|
379
421
|
return Ok(Some(frame.finish()?));
|
|
@@ -14,6 +14,6 @@ crate-type = ["cdylib"]
|
|
|
14
14
|
|
|
15
15
|
[dependencies]
|
|
16
16
|
polyxml = { path = "../polyxml-core" }
|
|
17
|
-
pyo3 = { version = "0.
|
|
17
|
+
pyo3 = { version = "0.29", features = ["abi3-py312", "extension-module"] }
|
|
18
18
|
lexical-core = "1.0"
|
|
19
19
|
smallvec = "1.16"
|
|
@@ -11,6 +11,8 @@ use std::sync::{Arc, RwLock};
|
|
|
11
11
|
use polyxml::schema::{FieldKind, FieldSchema, ModelSchema, ScalarType, ValueType};
|
|
12
12
|
use polyxml::value::PolyValue;
|
|
13
13
|
|
|
14
|
+
type PyObject = Py<PyAny>;
|
|
15
|
+
|
|
14
16
|
struct CachedFieldMeta {
|
|
15
17
|
py_string: Py<PyString>,
|
|
16
18
|
is_init: bool,
|
|
@@ -45,21 +47,31 @@ fn lookup_py_class<'py>(py: Python<'py>, schema_name: &str) -> Option<Bound<'py,
|
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
fn unwrap_optional_type<'py>(type_obj: &Bound<'py, PyAny>) -> Bound<'py, PyAny> {
|
|
48
|
-
if let Ok(origin) = type_obj.getattr("__origin__") {
|
|
50
|
+
let is_union = if let Ok(origin) = type_obj.getattr("__origin__") {
|
|
49
51
|
if let Ok(origin_name) = origin.getattr("__name__") {
|
|
50
52
|
let origin_str: String = origin_name.extract().unwrap_or_default();
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
53
|
+
origin_str == "Union" || origin_str == "UnionType"
|
|
54
|
+
} else {
|
|
55
|
+
false
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
type_obj
|
|
59
|
+
.get_type()
|
|
60
|
+
.name()
|
|
61
|
+
.map(|n| n == "UnionType")
|
|
62
|
+
.unwrap_or(false)
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
if is_union {
|
|
66
|
+
if let Ok(args) = type_obj.getattr("__args__") {
|
|
67
|
+
if let Ok(tuple) = args.cast::<PyTuple>() {
|
|
68
|
+
for arg in tuple.iter() {
|
|
69
|
+
let arg_name: String = arg
|
|
70
|
+
.getattr("__name__")
|
|
71
|
+
.and_then(|n| n.extract())
|
|
72
|
+
.unwrap_or_default();
|
|
73
|
+
if arg_name != "NoneType" {
|
|
74
|
+
return unwrap_optional_type(&arg);
|
|
63
75
|
}
|
|
64
76
|
}
|
|
65
77
|
}
|
|
@@ -69,10 +81,10 @@ fn unwrap_optional_type<'py>(type_obj: &Bound<'py, PyAny>) -> Bound<'py, PyAny>
|
|
|
69
81
|
}
|
|
70
82
|
|
|
71
83
|
fn is_enum_class<'py>(py: Python<'py>, cls: &Bound<'py, PyAny>) -> bool {
|
|
72
|
-
if let Ok(py_type) = cls.
|
|
84
|
+
if let Ok(py_type) = cls.cast::<PyType>() {
|
|
73
85
|
if let Ok(enum_module) = py.import("enum") {
|
|
74
86
|
if let Ok(enum_cls) = enum_module.getattr("Enum") {
|
|
75
|
-
if let Ok(py_enum) = enum_cls.
|
|
87
|
+
if let Ok(py_enum) = enum_cls.cast::<PyType>() {
|
|
76
88
|
return py_type.is_subclass(py_enum).unwrap_or(false);
|
|
77
89
|
}
|
|
78
90
|
}
|
|
@@ -108,26 +120,44 @@ fn resolve_scalar_type(py: Python<'_>, type_obj: &Bound<'_, PyAny>) -> PyResult<
|
|
|
108
120
|
}
|
|
109
121
|
|
|
110
122
|
fn resolve_value_type(py: Python<'_>, type_obj: &Bound<'_, PyAny>) -> PyResult<ValueType> {
|
|
111
|
-
// Handle typing.Optional / Union
|
|
112
|
-
if let Ok(origin) = type_obj.getattr("__origin__") {
|
|
123
|
+
// Handle typing.Optional / Union and PEP 604 UnionType
|
|
124
|
+
let is_union = if let Ok(origin) = type_obj.getattr("__origin__") {
|
|
113
125
|
if let Ok(origin_name) = origin.getattr("__name__") {
|
|
114
126
|
let origin_str: String = origin_name.extract().unwrap_or_default();
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
127
|
+
origin_str == "Union" || origin_str == "UnionType"
|
|
128
|
+
} else {
|
|
129
|
+
false
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
type_obj
|
|
133
|
+
.get_type()
|
|
134
|
+
.name()
|
|
135
|
+
.map(|n| n == "UnionType")
|
|
136
|
+
.unwrap_or(false)
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
if is_union {
|
|
140
|
+
if let Ok(args) = type_obj.getattr("__args__") {
|
|
141
|
+
if let Ok(tuple) = args.cast_into::<PyTuple>() {
|
|
142
|
+
for arg in tuple.iter() {
|
|
143
|
+
let arg_name: String = arg
|
|
144
|
+
.getattr("__name__")
|
|
145
|
+
.and_then(|n| n.extract())
|
|
146
|
+
.unwrap_or_default();
|
|
147
|
+
if arg_name != "NoneType" {
|
|
148
|
+
return resolve_value_type(py, &arg);
|
|
126
149
|
}
|
|
127
150
|
}
|
|
128
|
-
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if let Ok(origin) = type_obj.getattr("__origin__") {
|
|
156
|
+
if let Ok(origin_name) = origin.getattr("__name__") {
|
|
157
|
+
let origin_str: String = origin_name.extract().unwrap_or_default();
|
|
158
|
+
if origin_str == "list" || origin_str == "List" {
|
|
129
159
|
if let Ok(args) = type_obj.getattr("__args__") {
|
|
130
|
-
let tuple: Bound<'_, pyo3::types::PyTuple> = args.
|
|
160
|
+
let tuple: Bound<'_, pyo3::types::PyTuple> = args.cast_into()?;
|
|
131
161
|
if let Some(first) = tuple.iter().next() {
|
|
132
162
|
let inner = resolve_value_type(py, &first)?;
|
|
133
163
|
return Ok(ValueType::List(Box::new(inner)));
|
|
@@ -142,7 +172,7 @@ fn resolve_value_type(py: Python<'_>, type_obj: &Bound<'_, PyAny>) -> PyResult<V
|
|
|
142
172
|
|
|
143
173
|
// Check if target is a nested dataclass or Pydantic model
|
|
144
174
|
if type_obj.hasattr("__dataclass_fields__")? || type_obj.hasattr("model_fields")? {
|
|
145
|
-
if let Ok(py_type) = type_obj.
|
|
175
|
+
if let Ok(py_type) = type_obj.cast::<PyType>() {
|
|
146
176
|
let (nested_schema, _) = get_or_create_schema(py_type)?;
|
|
147
177
|
return Ok(ValueType::Nested(nested_schema));
|
|
148
178
|
}
|
|
@@ -165,10 +195,10 @@ fn extract_schema_from_class<'py>(
|
|
|
165
195
|
.ok()
|
|
166
196
|
.and_then(|m| m.getattr("get_type_hints").ok())
|
|
167
197
|
.and_then(|f| f.call1((cls,)).ok())
|
|
168
|
-
.and_then(|h| h.
|
|
198
|
+
.and_then(|h| h.cast_into::<PyDict>().ok());
|
|
169
199
|
|
|
170
200
|
if cls.hasattr("model_fields")? {
|
|
171
|
-
let model_fields: Bound<'py, PyDict> = cls.getattr("model_fields")?.
|
|
201
|
+
let model_fields: Bound<'py, PyDict> = cls.getattr("model_fields")?.cast_into()?;
|
|
172
202
|
for (name_obj, field_obj) in model_fields.iter() {
|
|
173
203
|
let py_name: String = name_obj.extract()?;
|
|
174
204
|
let py_string = PyString::new(py, &py_name).unbind();
|
|
@@ -221,7 +251,7 @@ fn extract_schema_from_class<'py>(
|
|
|
221
251
|
));
|
|
222
252
|
}
|
|
223
253
|
} else if cls.hasattr("__dataclass_fields__")? {
|
|
224
|
-
let fields: Bound<'py, PyDict> = cls.getattr("__dataclass_fields__")?.
|
|
254
|
+
let fields: Bound<'py, PyDict> = cls.getattr("__dataclass_fields__")?.cast_into()?;
|
|
225
255
|
for (name_obj, field_obj) in fields.iter() {
|
|
226
256
|
let py_name: String = name_obj.extract()?;
|
|
227
257
|
let py_string = PyString::new(py, &py_name).unbind();
|
|
@@ -631,7 +661,7 @@ fn py_to_poly_value<'py>(
|
|
|
631
661
|
}
|
|
632
662
|
},
|
|
633
663
|
ValueType::List(inner) => {
|
|
634
|
-
if let Ok(list) = val.
|
|
664
|
+
if let Ok(list) = val.cast::<PyList>() {
|
|
635
665
|
let mut poly_items = Vec::with_capacity(list.len());
|
|
636
666
|
let child_meta = if let ValueType::Nested(sub_schema) = inner.as_ref() {
|
|
637
667
|
lookup_cached_meta(&sub_schema.name)
|
|
@@ -329,3 +329,20 @@ def test_to_bytes_streams():
|
|
|
329
329
|
def test_to_bytes_invalid_type():
|
|
330
330
|
with pytest.raises(TypeError, match="Unsupported XML source type"):
|
|
331
331
|
polyxml.deserialize(12345, SimpleItem) # type: ignore[arg-type]
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
@dataclass
|
|
335
|
+
class UnionWrapper:
|
|
336
|
+
child: None | ChildNode = field(
|
|
337
|
+
default=None, metadata={"type": "Element", "name": "child"}
|
|
338
|
+
)
|
|
339
|
+
count: int | None = field(default=None, metadata={"type": "Element"})
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def test_pep604_union_nested_dataclass():
|
|
343
|
+
xml = "<UnionWrapper><child><tag>Payload</tag></child><count>42</count></UnionWrapper>"
|
|
344
|
+
res = polyxml.deserialize(xml, UnionWrapper)
|
|
345
|
+
assert res.child is not None
|
|
346
|
+
assert res.child.tag == "Payload"
|
|
347
|
+
assert res.count == 42
|
|
348
|
+
|
|
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
|