polyxml 0.2.1__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.2.1 → polyxml-0.4.0}/Cargo.lock +89 -81
- {polyxml-0.2.1 → polyxml-0.4.0}/Cargo.toml +1 -1
- {polyxml-0.2.1 → polyxml-0.4.0}/PKG-INFO +1 -1
- {polyxml-0.2.1 → polyxml-0.4.0}/crates/polyxml-core/Cargo.toml +2 -2
- {polyxml-0.2.1 → polyxml-0.4.0}/crates/polyxml-core/benches/core_benchmarks.rs +3 -1
- {polyxml-0.2.1 → polyxml-0.4.0}/crates/polyxml-core/src/error.rs +4 -1
- {polyxml-0.2.1 → polyxml-0.4.0}/crates/polyxml-core/src/parser.rs +58 -16
- {polyxml-0.2.1 → polyxml-0.4.0}/crates/polyxml-python/Cargo.toml +1 -2
- {polyxml-0.2.1 → polyxml-0.4.0}/crates/polyxml-python/src/lib.rs +87 -56
- {polyxml-0.2.1 → polyxml-0.4.0}/crates/polyxml-python/tests/test_polyxml.py +17 -0
- {polyxml-0.2.1 → polyxml-0.4.0}/pyproject.toml +1 -1
- {polyxml-0.2.1 → polyxml-0.4.0}/README.md +0 -0
- {polyxml-0.2.1 → polyxml-0.4.0}/crates/polyxml-core/README.md +0 -0
- {polyxml-0.2.1 → polyxml-0.4.0}/crates/polyxml-core/src/converters.rs +0 -0
- {polyxml-0.2.1 → polyxml-0.4.0}/crates/polyxml-core/src/lib.rs +0 -0
- {polyxml-0.2.1 → polyxml-0.4.0}/crates/polyxml-core/src/schema.rs +0 -0
- {polyxml-0.2.1 → polyxml-0.4.0}/crates/polyxml-core/src/serializer.rs +0 -0
- {polyxml-0.2.1 → polyxml-0.4.0}/crates/polyxml-core/src/value.rs +0 -0
- {polyxml-0.2.1 → polyxml-0.4.0}/crates/polyxml-core/tests/test_core.rs +0 -0
- {polyxml-0.2.1 → polyxml-0.4.0}/crates/polyxml-core/tests/test_security.rs +0 -0
- {polyxml-0.2.1 → polyxml-0.4.0}/crates/polyxml-python/README.md +0 -0
- {polyxml-0.2.1 → polyxml-0.4.0}/python/polyxml/__init__.py +0 -0
- {polyxml-0.2.1 → 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,23 +129,22 @@ 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",
|
|
120
140
|
"clap",
|
|
121
141
|
"criterion-plot",
|
|
122
|
-
"is-terminal",
|
|
123
142
|
"itertools",
|
|
124
143
|
"num-traits",
|
|
125
|
-
"once_cell",
|
|
126
144
|
"oorandom",
|
|
145
|
+
"page_size",
|
|
127
146
|
"regex",
|
|
128
147
|
"serde",
|
|
129
|
-
"serde_derive",
|
|
130
148
|
"serde_json",
|
|
131
149
|
"tinytemplate",
|
|
132
150
|
"walkdir",
|
|
@@ -134,9 +152,9 @@ dependencies = [
|
|
|
134
152
|
|
|
135
153
|
[[package]]
|
|
136
154
|
name = "criterion-plot"
|
|
137
|
-
version = "0.
|
|
155
|
+
version = "0.8.2"
|
|
138
156
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
139
|
-
checksum = "
|
|
157
|
+
checksum = "d8d80a2f4f5b554395e47b5d8305bc3d27813bacb73493eb1001e8f76dae29ea"
|
|
140
158
|
dependencies = [
|
|
141
159
|
"cast",
|
|
142
160
|
"itertools",
|
|
@@ -170,6 +188,12 @@ version = "1.18.0"
|
|
|
170
188
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
171
189
|
checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34"
|
|
172
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
|
+
|
|
173
197
|
[[package]]
|
|
174
198
|
name = "half"
|
|
175
199
|
version = "2.7.1"
|
|
@@ -187,37 +211,11 @@ version = "0.5.0"
|
|
|
187
211
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
188
212
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|
189
213
|
|
|
190
|
-
[[package]]
|
|
191
|
-
name = "hermit-abi"
|
|
192
|
-
version = "0.5.3"
|
|
193
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
194
|
-
checksum = "e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284"
|
|
195
|
-
|
|
196
|
-
[[package]]
|
|
197
|
-
name = "indoc"
|
|
198
|
-
version = "2.0.7"
|
|
199
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
200
|
-
checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
|
|
201
|
-
dependencies = [
|
|
202
|
-
"rustversion",
|
|
203
|
-
]
|
|
204
|
-
|
|
205
|
-
[[package]]
|
|
206
|
-
name = "is-terminal"
|
|
207
|
-
version = "0.4.17"
|
|
208
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
209
|
-
checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46"
|
|
210
|
-
dependencies = [
|
|
211
|
-
"hermit-abi",
|
|
212
|
-
"libc",
|
|
213
|
-
"windows-sys",
|
|
214
|
-
]
|
|
215
|
-
|
|
216
214
|
[[package]]
|
|
217
215
|
name = "itertools"
|
|
218
|
-
version = "0.
|
|
216
|
+
version = "0.13.0"
|
|
219
217
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
220
|
-
checksum = "
|
|
218
|
+
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
|
221
219
|
dependencies = [
|
|
222
220
|
"either",
|
|
223
221
|
]
|
|
@@ -307,15 +305,6 @@ version = "2.8.3"
|
|
|
307
305
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
308
306
|
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
|
309
307
|
|
|
310
|
-
[[package]]
|
|
311
|
-
name = "memoffset"
|
|
312
|
-
version = "0.9.1"
|
|
313
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
314
|
-
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
|
315
|
-
dependencies = [
|
|
316
|
-
"autocfg",
|
|
317
|
-
]
|
|
318
|
-
|
|
319
308
|
[[package]]
|
|
320
309
|
name = "napi"
|
|
321
310
|
version = "2.16.17"
|
|
@@ -394,9 +383,19 @@ version = "11.1.5"
|
|
|
394
383
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
395
384
|
checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
|
|
396
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
|
+
|
|
397
396
|
[[package]]
|
|
398
397
|
name = "polyxml"
|
|
399
|
-
version = "0.
|
|
398
|
+
version = "0.4.0"
|
|
400
399
|
dependencies = [
|
|
401
400
|
"criterion",
|
|
402
401
|
"lexical-core",
|
|
@@ -409,14 +408,14 @@ dependencies = [
|
|
|
409
408
|
|
|
410
409
|
[[package]]
|
|
411
410
|
name = "polyxml-c"
|
|
412
|
-
version = "0.
|
|
411
|
+
version = "0.4.0"
|
|
413
412
|
dependencies = [
|
|
414
413
|
"polyxml",
|
|
415
414
|
]
|
|
416
415
|
|
|
417
416
|
[[package]]
|
|
418
417
|
name = "polyxml-js"
|
|
419
|
-
version = "0.
|
|
418
|
+
version = "0.4.0"
|
|
420
419
|
dependencies = [
|
|
421
420
|
"napi",
|
|
422
421
|
"napi-build",
|
|
@@ -426,12 +425,11 @@ dependencies = [
|
|
|
426
425
|
|
|
427
426
|
[[package]]
|
|
428
427
|
name = "polyxml-python"
|
|
429
|
-
version = "0.
|
|
428
|
+
version = "0.4.0"
|
|
430
429
|
dependencies = [
|
|
431
430
|
"lexical-core",
|
|
432
431
|
"polyxml",
|
|
433
432
|
"pyo3",
|
|
434
|
-
"quick-xml",
|
|
435
433
|
"smallvec",
|
|
436
434
|
]
|
|
437
435
|
|
|
@@ -462,37 +460,32 @@ dependencies = [
|
|
|
462
460
|
|
|
463
461
|
[[package]]
|
|
464
462
|
name = "pyo3"
|
|
465
|
-
version = "0.
|
|
463
|
+
version = "0.29.2"
|
|
466
464
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
467
|
-
checksum = "
|
|
465
|
+
checksum = "4688ddedf473e32662b9b067670129a8afb8c18e351482c70d62ba4a88171e8b"
|
|
468
466
|
dependencies = [
|
|
469
|
-
"cfg-if",
|
|
470
|
-
"indoc",
|
|
471
467
|
"libc",
|
|
472
|
-
"memoffset",
|
|
473
468
|
"once_cell",
|
|
474
469
|
"portable-atomic",
|
|
475
470
|
"pyo3-build-config",
|
|
476
471
|
"pyo3-ffi",
|
|
477
472
|
"pyo3-macros",
|
|
478
|
-
"unindent",
|
|
479
473
|
]
|
|
480
474
|
|
|
481
475
|
[[package]]
|
|
482
476
|
name = "pyo3-build-config"
|
|
483
|
-
version = "0.
|
|
477
|
+
version = "0.29.2"
|
|
484
478
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
485
|
-
checksum = "
|
|
479
|
+
checksum = "f41027e41b4bd03f6e60f9f417fe24a6341a6bb744edd62b6f709f2a52ea30e9"
|
|
486
480
|
dependencies = [
|
|
487
|
-
"once_cell",
|
|
488
481
|
"target-lexicon",
|
|
489
482
|
]
|
|
490
483
|
|
|
491
484
|
[[package]]
|
|
492
485
|
name = "pyo3-ffi"
|
|
493
|
-
version = "0.
|
|
486
|
+
version = "0.29.2"
|
|
494
487
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
495
|
-
checksum = "
|
|
488
|
+
checksum = "e591a95526fead067432c3b3a33fc74770b87b1e04e73671090d9c2055a2b327"
|
|
496
489
|
dependencies = [
|
|
497
490
|
"libc",
|
|
498
491
|
"pyo3-build-config",
|
|
@@ -500,9 +493,9 @@ dependencies = [
|
|
|
500
493
|
|
|
501
494
|
[[package]]
|
|
502
495
|
name = "pyo3-macros"
|
|
503
|
-
version = "0.
|
|
496
|
+
version = "0.29.2"
|
|
504
497
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
505
|
-
checksum = "
|
|
498
|
+
checksum = "73225868fc1cd84eef2c3c230ddb91273bf1de46aeb8a4248da76d32a0924a1c"
|
|
506
499
|
dependencies = [
|
|
507
500
|
"proc-macro2",
|
|
508
501
|
"pyo3-macros-backend",
|
|
@@ -512,22 +505,21 @@ dependencies = [
|
|
|
512
505
|
|
|
513
506
|
[[package]]
|
|
514
507
|
name = "pyo3-macros-backend"
|
|
515
|
-
version = "0.
|
|
508
|
+
version = "0.29.2"
|
|
516
509
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
517
|
-
checksum = "
|
|
510
|
+
checksum = "571575aa3749fa6216757dd47d2a3e7ef360f329a40f0666a9fbd14889024952"
|
|
518
511
|
dependencies = [
|
|
519
512
|
"heck",
|
|
520
513
|
"proc-macro2",
|
|
521
|
-
"pyo3-build-config",
|
|
522
514
|
"quote",
|
|
523
515
|
"syn 2.0.119",
|
|
524
516
|
]
|
|
525
517
|
|
|
526
518
|
[[package]]
|
|
527
519
|
name = "quick-xml"
|
|
528
|
-
version = "0.
|
|
520
|
+
version = "0.42.0"
|
|
529
521
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
530
|
-
checksum = "
|
|
522
|
+
checksum = "41b1177fdf999d2321d3fb46ff47159d9c1fb9ad66a4879f8c50a0b504615e9b"
|
|
531
523
|
dependencies = [
|
|
532
524
|
"memchr",
|
|
533
525
|
"serde",
|
|
@@ -571,12 +563,6 @@ version = "0.8.11"
|
|
|
571
563
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
572
564
|
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
|
573
565
|
|
|
574
|
-
[[package]]
|
|
575
|
-
name = "rustversion"
|
|
576
|
-
version = "1.0.23"
|
|
577
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
578
|
-
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
|
|
579
|
-
|
|
580
566
|
[[package]]
|
|
581
567
|
name = "same-file"
|
|
582
568
|
version = "1.0.6"
|
|
@@ -635,6 +621,12 @@ dependencies = [
|
|
|
635
621
|
"zmij",
|
|
636
622
|
]
|
|
637
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
|
+
|
|
638
630
|
[[package]]
|
|
639
631
|
name = "smallvec"
|
|
640
632
|
version = "1.16.0"
|
|
@@ -665,9 +657,9 @@ dependencies = [
|
|
|
665
657
|
|
|
666
658
|
[[package]]
|
|
667
659
|
name = "target-lexicon"
|
|
668
|
-
version = "0.
|
|
660
|
+
version = "0.13.5"
|
|
669
661
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
670
|
-
checksum = "
|
|
662
|
+
checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
|
671
663
|
|
|
672
664
|
[[package]]
|
|
673
665
|
name = "thiserror"
|
|
@@ -711,12 +703,6 @@ version = "1.13.3"
|
|
|
711
703
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
712
704
|
checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
|
|
713
705
|
|
|
714
|
-
[[package]]
|
|
715
|
-
name = "unindent"
|
|
716
|
-
version = "0.2.4"
|
|
717
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
718
|
-
checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
|
|
719
|
-
|
|
720
706
|
[[package]]
|
|
721
707
|
name = "walkdir"
|
|
722
708
|
version = "2.5.0"
|
|
@@ -727,6 +713,22 @@ dependencies = [
|
|
|
727
713
|
"winapi-util",
|
|
728
714
|
]
|
|
729
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
|
+
|
|
730
732
|
[[package]]
|
|
731
733
|
name = "winapi-util"
|
|
732
734
|
version = "0.1.11"
|
|
@@ -736,6 +738,12 @@ dependencies = [
|
|
|
736
738
|
"windows-sys",
|
|
737
739
|
]
|
|
738
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
|
+
|
|
739
747
|
[[package]]
|
|
740
748
|
name = "windows-link"
|
|
741
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]]
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
use
|
|
1
|
+
use std::hint::black_box;
|
|
2
|
+
|
|
3
|
+
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
|
|
2
4
|
use polyxml::schema::{FieldKind, FieldSchema, ModelSchema, ScalarType, ValueType};
|
|
3
5
|
use polyxml::{deserialize, serialize};
|
|
4
6
|
use std::sync::Arc;
|
|
@@ -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,7 +14,6 @@ crate-type = ["cdylib"]
|
|
|
14
14
|
|
|
15
15
|
[dependencies]
|
|
16
16
|
polyxml = { path = "../polyxml-core" }
|
|
17
|
-
pyo3 = { version = "0.
|
|
18
|
-
quick-xml = { version = "0.36" }
|
|
17
|
+
pyo3 = { version = "0.29", features = ["abi3-py312", "extension-module"] }
|
|
19
18
|
lexical-core = "1.0"
|
|
20
19
|
smallvec = "1.16"
|
|
@@ -4,12 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
use pyo3::prelude::*;
|
|
6
6
|
use pyo3::types::{PyBytes, PyDict, PyList, PyString, PyTuple, PyType};
|
|
7
|
+
use pyo3::IntoPyObjectExt;
|
|
7
8
|
use std::collections::HashMap;
|
|
8
9
|
use std::sync::{Arc, RwLock};
|
|
9
10
|
|
|
10
11
|
use polyxml::schema::{FieldKind, FieldSchema, ModelSchema, ScalarType, ValueType};
|
|
11
12
|
use polyxml::value::PolyValue;
|
|
12
13
|
|
|
14
|
+
type PyObject = Py<PyAny>;
|
|
15
|
+
|
|
13
16
|
struct CachedFieldMeta {
|
|
14
17
|
py_string: Py<PyString>,
|
|
15
18
|
is_init: bool,
|
|
@@ -44,21 +47,31 @@ fn lookup_py_class<'py>(py: Python<'py>, schema_name: &str) -> Option<Bound<'py,
|
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
fn unwrap_optional_type<'py>(type_obj: &Bound<'py, PyAny>) -> Bound<'py, PyAny> {
|
|
47
|
-
if let Ok(origin) = type_obj.getattr("__origin__") {
|
|
50
|
+
let is_union = if let Ok(origin) = type_obj.getattr("__origin__") {
|
|
48
51
|
if let Ok(origin_name) = origin.getattr("__name__") {
|
|
49
52
|
let origin_str: String = origin_name.extract().unwrap_or_default();
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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);
|
|
62
75
|
}
|
|
63
76
|
}
|
|
64
77
|
}
|
|
@@ -68,10 +81,10 @@ fn unwrap_optional_type<'py>(type_obj: &Bound<'py, PyAny>) -> Bound<'py, PyAny>
|
|
|
68
81
|
}
|
|
69
82
|
|
|
70
83
|
fn is_enum_class<'py>(py: Python<'py>, cls: &Bound<'py, PyAny>) -> bool {
|
|
71
|
-
if let Ok(py_type) = cls.
|
|
72
|
-
if let Ok(enum_module) = py.
|
|
84
|
+
if let Ok(py_type) = cls.cast::<PyType>() {
|
|
85
|
+
if let Ok(enum_module) = py.import("enum") {
|
|
73
86
|
if let Ok(enum_cls) = enum_module.getattr("Enum") {
|
|
74
|
-
if let Ok(py_enum) = enum_cls.
|
|
87
|
+
if let Ok(py_enum) = enum_cls.cast::<PyType>() {
|
|
75
88
|
return py_type.is_subclass(py_enum).unwrap_or(false);
|
|
76
89
|
}
|
|
77
90
|
}
|
|
@@ -107,26 +120,44 @@ fn resolve_scalar_type(py: Python<'_>, type_obj: &Bound<'_, PyAny>) -> PyResult<
|
|
|
107
120
|
}
|
|
108
121
|
|
|
109
122
|
fn resolve_value_type(py: Python<'_>, type_obj: &Bound<'_, PyAny>) -> PyResult<ValueType> {
|
|
110
|
-
// Handle typing.Optional / Union
|
|
111
|
-
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__") {
|
|
112
125
|
if let Ok(origin_name) = origin.getattr("__name__") {
|
|
113
126
|
let origin_str: String = origin_name.extract().unwrap_or_default();
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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);
|
|
125
149
|
}
|
|
126
150
|
}
|
|
127
|
-
}
|
|
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" {
|
|
128
159
|
if let Ok(args) = type_obj.getattr("__args__") {
|
|
129
|
-
let tuple: Bound<'_, pyo3::types::PyTuple> = args.
|
|
160
|
+
let tuple: Bound<'_, pyo3::types::PyTuple> = args.cast_into()?;
|
|
130
161
|
if let Some(first) = tuple.iter().next() {
|
|
131
162
|
let inner = resolve_value_type(py, &first)?;
|
|
132
163
|
return Ok(ValueType::List(Box::new(inner)));
|
|
@@ -141,7 +172,7 @@ fn resolve_value_type(py: Python<'_>, type_obj: &Bound<'_, PyAny>) -> PyResult<V
|
|
|
141
172
|
|
|
142
173
|
// Check if target is a nested dataclass or Pydantic model
|
|
143
174
|
if type_obj.hasattr("__dataclass_fields__")? || type_obj.hasattr("model_fields")? {
|
|
144
|
-
if let Ok(py_type) = type_obj.
|
|
175
|
+
if let Ok(py_type) = type_obj.cast::<PyType>() {
|
|
145
176
|
let (nested_schema, _) = get_or_create_schema(py_type)?;
|
|
146
177
|
return Ok(ValueType::Nested(nested_schema));
|
|
147
178
|
}
|
|
@@ -160,17 +191,17 @@ fn extract_schema_from_class<'py>(
|
|
|
160
191
|
let mut cached_fields = Vec::new();
|
|
161
192
|
|
|
162
193
|
let type_hints: Option<Bound<'py, PyDict>> = py
|
|
163
|
-
.
|
|
194
|
+
.import("typing")
|
|
164
195
|
.ok()
|
|
165
196
|
.and_then(|m| m.getattr("get_type_hints").ok())
|
|
166
197
|
.and_then(|f| f.call1((cls,)).ok())
|
|
167
|
-
.and_then(|h| h.
|
|
198
|
+
.and_then(|h| h.cast_into::<PyDict>().ok());
|
|
168
199
|
|
|
169
200
|
if cls.hasattr("model_fields")? {
|
|
170
|
-
let model_fields: Bound<'py, PyDict> = cls.getattr("model_fields")?.
|
|
201
|
+
let model_fields: Bound<'py, PyDict> = cls.getattr("model_fields")?.cast_into()?;
|
|
171
202
|
for (name_obj, field_obj) in model_fields.iter() {
|
|
172
203
|
let py_name: String = name_obj.extract()?;
|
|
173
|
-
let py_string = PyString::
|
|
204
|
+
let py_string = PyString::new(py, &py_name).unbind();
|
|
174
205
|
let mut xml_name = py_name.clone();
|
|
175
206
|
let mut kind = FieldKind::Element;
|
|
176
207
|
|
|
@@ -220,10 +251,10 @@ fn extract_schema_from_class<'py>(
|
|
|
220
251
|
));
|
|
221
252
|
}
|
|
222
253
|
} else if cls.hasattr("__dataclass_fields__")? {
|
|
223
|
-
let fields: Bound<'py, PyDict> = cls.getattr("__dataclass_fields__")?.
|
|
254
|
+
let fields: Bound<'py, PyDict> = cls.getattr("__dataclass_fields__")?.cast_into()?;
|
|
224
255
|
for (name_obj, field_obj) in fields.iter() {
|
|
225
256
|
let py_name: String = name_obj.extract()?;
|
|
226
|
-
let py_string = PyString::
|
|
257
|
+
let py_string = PyString::new(py, &py_name).unbind();
|
|
227
258
|
let mut xml_name = py_name.clone();
|
|
228
259
|
let mut kind = FieldKind::Element;
|
|
229
260
|
|
|
@@ -333,7 +364,7 @@ fn convert_scalar_to_py<'py>(
|
|
|
333
364
|
) -> PyResult<PyObject> {
|
|
334
365
|
match val {
|
|
335
366
|
PolyValue::Null => Ok(py.None()),
|
|
336
|
-
PolyValue::Bool(b) =>
|
|
367
|
+
PolyValue::Bool(b) => b.into_py_any(py),
|
|
337
368
|
PolyValue::Int(i) => {
|
|
338
369
|
if let Some(target_type) = py_type_opt {
|
|
339
370
|
if is_enum_class(py, target_type) {
|
|
@@ -342,9 +373,9 @@ fn convert_scalar_to_py<'py>(
|
|
|
342
373
|
}
|
|
343
374
|
}
|
|
344
375
|
}
|
|
345
|
-
|
|
376
|
+
i.into_py_any(py)
|
|
346
377
|
}
|
|
347
|
-
PolyValue::Float(f) =>
|
|
378
|
+
PolyValue::Float(f) => f.into_py_any(py),
|
|
348
379
|
PolyValue::String(s) => {
|
|
349
380
|
if let Some(target_type) = py_type_opt {
|
|
350
381
|
if is_enum_class(py, target_type) {
|
|
@@ -368,7 +399,7 @@ fn convert_scalar_to_py<'py>(
|
|
|
368
399
|
|
|
369
400
|
match type_name.as_str() {
|
|
370
401
|
"Decimal" => {
|
|
371
|
-
let decimal_cls = py.
|
|
402
|
+
let decimal_cls = py.import("decimal")?.getattr("Decimal")?;
|
|
372
403
|
let dec = decimal_cls.call1((s.as_str(),))?;
|
|
373
404
|
return Ok(dec.unbind());
|
|
374
405
|
}
|
|
@@ -376,7 +407,7 @@ fn convert_scalar_to_py<'py>(
|
|
|
376
407
|
if let Ok(val) = target_type.call_method1("from_string", (s.as_str(),)) {
|
|
377
408
|
return Ok(val.unbind());
|
|
378
409
|
}
|
|
379
|
-
if let Ok(datatype_mod) = py.
|
|
410
|
+
if let Ok(datatype_mod) = py.import("pyxsdata.models.datatype") {
|
|
380
411
|
if let Ok(cls) = datatype_mod.getattr(type_name.as_str()) {
|
|
381
412
|
if let Ok(val) = cls.call_method1("from_string", (s.as_str(),)) {
|
|
382
413
|
return Ok(val.unbind());
|
|
@@ -392,7 +423,7 @@ fn convert_scalar_to_py<'py>(
|
|
|
392
423
|
"XmlTime" => "time",
|
|
393
424
|
_ => "",
|
|
394
425
|
};
|
|
395
|
-
if let Ok(datetime_mod) = py.
|
|
426
|
+
if let Ok(datetime_mod) = py.import("datetime") {
|
|
396
427
|
if let Ok(cls) = datetime_mod.getattr(mod_name) {
|
|
397
428
|
if let Ok(val) = cls.call_method1("fromisoformat", (s.as_str(),)) {
|
|
398
429
|
return Ok(val.unbind());
|
|
@@ -404,7 +435,7 @@ fn convert_scalar_to_py<'py>(
|
|
|
404
435
|
if let Ok(val) = target_type.call1((s.as_str(),)) {
|
|
405
436
|
return Ok(val.unbind());
|
|
406
437
|
}
|
|
407
|
-
if let Ok(datatype_mod) = py.
|
|
438
|
+
if let Ok(datatype_mod) = py.import("pyxsdata.models.datatype") {
|
|
408
439
|
if let Ok(cls) = datatype_mod.getattr("XmlDuration") {
|
|
409
440
|
if let Ok(val) = cls.call1((s.as_str(),)) {
|
|
410
441
|
return Ok(val.unbind());
|
|
@@ -415,7 +446,7 @@ fn convert_scalar_to_py<'py>(
|
|
|
415
446
|
_ => {}
|
|
416
447
|
}
|
|
417
448
|
}
|
|
418
|
-
Ok(PyString::
|
|
449
|
+
Ok(PyString::new(py, s).into_any().unbind())
|
|
419
450
|
}
|
|
420
451
|
_ => Ok(py.None()),
|
|
421
452
|
}
|
|
@@ -430,7 +461,7 @@ fn poly_value_to_py<'py>(
|
|
|
430
461
|
) -> PyResult<PyObject> {
|
|
431
462
|
match val {
|
|
432
463
|
PolyValue::Null => Ok(py.None()),
|
|
433
|
-
PolyValue::Bool(b) =>
|
|
464
|
+
PolyValue::Bool(b) => b.into_py_any(py),
|
|
434
465
|
PolyValue::Int(_) | PolyValue::Float(_) | PolyValue::String(_) => {
|
|
435
466
|
let st = match val_type {
|
|
436
467
|
ValueType::Scalar(ref s) => s,
|
|
@@ -456,10 +487,10 @@ fn poly_value_to_py<'py>(
|
|
|
456
487
|
poly_value_to_py(py, item, inner_type, inner_cls.as_ref(), field_meta)?;
|
|
457
488
|
py_items.push(py_item);
|
|
458
489
|
}
|
|
459
|
-
let py_list = PyList::
|
|
490
|
+
let py_list = PyList::new(py, &py_items)?;
|
|
460
491
|
Ok(py_list.into_any().unbind())
|
|
461
492
|
} else {
|
|
462
|
-
let py_list = PyList::
|
|
493
|
+
let py_list = PyList::empty(py);
|
|
463
494
|
Ok(py_list.into_any().unbind())
|
|
464
495
|
}
|
|
465
496
|
}
|
|
@@ -508,7 +539,7 @@ fn poly_value_to_py<'py>(
|
|
|
508
539
|
}
|
|
509
540
|
}
|
|
510
541
|
if all_found {
|
|
511
|
-
let tuple = pyo3::types::PyTuple::
|
|
542
|
+
let tuple = pyo3::types::PyTuple::new(py, &args_vec)?;
|
|
512
543
|
if let Ok(instance) = target_cls.call1(tuple) {
|
|
513
544
|
return Ok(instance.unbind());
|
|
514
545
|
}
|
|
@@ -517,7 +548,7 @@ fn poly_value_to_py<'py>(
|
|
|
517
548
|
}
|
|
518
549
|
|
|
519
550
|
// Method 2: Keyword arguments with init=False post-init support
|
|
520
|
-
let kwargs = PyDict::
|
|
551
|
+
let kwargs = PyDict::new(py);
|
|
521
552
|
let mut post_init_fields: Vec<(Py<PyString>, PyObject)> = Vec::new();
|
|
522
553
|
|
|
523
554
|
for (i, field) in schema.fields.iter().enumerate() {
|
|
@@ -546,7 +577,7 @@ fn poly_value_to_py<'py>(
|
|
|
546
577
|
kwargs.set_item(&field.name, py_val)?;
|
|
547
578
|
} else {
|
|
548
579
|
post_init_fields
|
|
549
|
-
.push((PyString::
|
|
580
|
+
.push((PyString::new(py, &field.name).unbind(), py_val));
|
|
550
581
|
}
|
|
551
582
|
}
|
|
552
583
|
}
|
|
@@ -556,7 +587,7 @@ fn poly_value_to_py<'py>(
|
|
|
556
587
|
}
|
|
557
588
|
return Ok(instance.unbind());
|
|
558
589
|
}
|
|
559
|
-
let dict = PyDict::
|
|
590
|
+
let dict = PyDict::new(py);
|
|
560
591
|
for (k, v) in map {
|
|
561
592
|
let py_v =
|
|
562
593
|
poly_value_to_py(py, v, &ValueType::Scalar(ScalarType::Any), None, None)?;
|
|
@@ -565,7 +596,7 @@ fn poly_value_to_py<'py>(
|
|
|
565
596
|
let instance = target_cls.call((), Some(&dict))?;
|
|
566
597
|
Ok(instance.unbind())
|
|
567
598
|
} else {
|
|
568
|
-
let dict = PyDict::
|
|
599
|
+
let dict = PyDict::new(py);
|
|
569
600
|
for (k, v) in map {
|
|
570
601
|
let py_v =
|
|
571
602
|
poly_value_to_py(py, v, &ValueType::Scalar(ScalarType::Any), None, None)?;
|
|
@@ -630,7 +661,7 @@ fn py_to_poly_value<'py>(
|
|
|
630
661
|
}
|
|
631
662
|
},
|
|
632
663
|
ValueType::List(inner) => {
|
|
633
|
-
if let Ok(list) = val.
|
|
664
|
+
if let Ok(list) = val.cast::<PyList>() {
|
|
634
665
|
let mut poly_items = Vec::with_capacity(list.len());
|
|
635
666
|
let child_meta = if let ValueType::Nested(sub_schema) = inner.as_ref() {
|
|
636
667
|
lookup_cached_meta(&sub_schema.name)
|
|
@@ -763,7 +794,7 @@ fn serialize<'py>(
|
|
|
763
794
|
let bytes = polyxml::serialize(root_name, &poly_val, &meta.schema, indent)
|
|
764
795
|
.map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string()))?;
|
|
765
796
|
|
|
766
|
-
Ok(PyBytes::
|
|
797
|
+
Ok(PyBytes::new(py, &bytes))
|
|
767
798
|
}
|
|
768
799
|
|
|
769
800
|
#[pyfunction]
|
|
@@ -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
|