lutaml-xsd 1.2.0 → 1.2.2
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +45 -45
- data/TODO.cleanup.md +12 -2
- data/TODO.conformance.md +37 -35
- data/lib/lutaml/xsd/json_schema_exporter.rb +35 -8
- data/lib/lutaml/xsd/rng_to_xsd_converter.rb +67 -46
- data/lib/lutaml/xsd/schema_repository/type_index.rb +2 -2
- data/lib/lutaml/xsd/spa/schema_serializer.rb +22 -25
- data/lib/lutaml/xsd/validation/rules/attribute_validation_rule.rb +27 -1
- data/lib/lutaml/xsd/validation/rules/content_model_validation_rule.rb +28 -23
- data/lib/lutaml/xsd/validation/rules/identity_constraints_rule.rb +28 -5
- data/lib/lutaml/xsd/validation/schema_definition_resolver.rb +57 -27
- data/lib/lutaml/xsd/validation/unique_particle_attribution.rb +302 -0
- data/lib/lutaml/xsd/validation/validation_rule.rb +5 -5
- data/lib/lutaml/xsd/version.rb +1 -1
- data/lutaml-xsd.gemspec +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a22b97347b4ad22fc7698a047a1037f4b16c5a6bad3ed0154701908f0052ea29
|
|
4
|
+
data.tar.gz: 0a3d2bd48d987524cbdc427adefcdc128ebcd089a2ed5e25203c28ff27ba4416
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 304b7555580c9bd6b79464b3b2d9ff73d9c69177790fbcd8bb97b9b0438fdc96b19ce19bdbaba4a9a3c2c6446402e81ee457614cd3e33c3dac35e02b2c948d74
|
|
7
|
+
data.tar.gz: 39593e00b27fb41c0fe4e374b682c4172dabcf56cd8250e2917f76c135e9f6421e1c65eaca4c0c3896a7d03bae1395fa18c8a1b6f8e38bb3e23cbfa4e195ef6a
|
data/Gemfile.lock
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
lutaml-xsd (1.1
|
|
4
|
+
lutaml-xsd (1.2.1)
|
|
5
5
|
liquid (>= 4.0, < 6.0)
|
|
6
|
-
lutaml-model (~> 0.8.
|
|
6
|
+
lutaml-model (~> 0.8.123)
|
|
7
7
|
lutaml-store (~> 0.3)
|
|
8
8
|
moxml (~> 0.5.147)
|
|
9
9
|
paint (~> 2.3)
|
|
@@ -20,7 +20,7 @@ GEM
|
|
|
20
20
|
ast (2.4.3)
|
|
21
21
|
base64 (0.3.0)
|
|
22
22
|
bigdecimal (4.1.3)
|
|
23
|
-
canon (0.3.
|
|
23
|
+
canon (0.3.110)
|
|
24
24
|
diff-lcs
|
|
25
25
|
json
|
|
26
26
|
leptris (~> 1.9.376)
|
|
@@ -46,29 +46,29 @@ GEM
|
|
|
46
46
|
fiddle (1.1.8)
|
|
47
47
|
json (3.1.0)
|
|
48
48
|
language_server-protocol (3.17.0.6)
|
|
49
|
-
leptris (1.9.
|
|
49
|
+
leptris (1.9.380.2-aarch64-linux)
|
|
50
50
|
ffi (~> 1.15)
|
|
51
|
-
leptris (1.9.
|
|
51
|
+
leptris (1.9.380.2-aarch64-linux-musl)
|
|
52
52
|
ffi (~> 1.15)
|
|
53
|
-
leptris (1.9.
|
|
53
|
+
leptris (1.9.380.2-arm-linux)
|
|
54
54
|
ffi (~> 1.15)
|
|
55
|
-
leptris (1.9.
|
|
55
|
+
leptris (1.9.380.2-arm-linux-musl)
|
|
56
56
|
ffi (~> 1.15)
|
|
57
|
-
leptris (1.9.
|
|
57
|
+
leptris (1.9.380.2-arm64-darwin)
|
|
58
58
|
ffi (~> 1.15)
|
|
59
|
-
leptris (1.9.
|
|
59
|
+
leptris (1.9.380.2-x64-mingw-ucrt)
|
|
60
60
|
ffi (~> 1.15)
|
|
61
|
-
leptris (1.9.
|
|
61
|
+
leptris (1.9.380.2-x86_64-darwin)
|
|
62
62
|
ffi (~> 1.15)
|
|
63
|
-
leptris (1.9.
|
|
63
|
+
leptris (1.9.380.2-x86_64-linux)
|
|
64
64
|
ffi (~> 1.15)
|
|
65
|
-
leptris (1.9.
|
|
65
|
+
leptris (1.9.380.2-x86_64-linux-musl)
|
|
66
66
|
ffi (~> 1.15)
|
|
67
67
|
lint_roller (1.1.0)
|
|
68
68
|
liquid (5.14.0)
|
|
69
69
|
bigdecimal
|
|
70
70
|
strscan (>= 3.1.1)
|
|
71
|
-
lutaml-model (0.8.
|
|
71
|
+
lutaml-model (0.8.123)
|
|
72
72
|
base64 (>= 0.1.1)
|
|
73
73
|
bigdecimal
|
|
74
74
|
canon
|
|
@@ -82,7 +82,7 @@ GEM
|
|
|
82
82
|
lutaml-model (~> 0.8.15)
|
|
83
83
|
rubyzip (>= 2.3, < 4)
|
|
84
84
|
memo_wise (1.13.0)
|
|
85
|
-
moxml (0.5.
|
|
85
|
+
moxml (0.5.153)
|
|
86
86
|
nokogiri (1.19.4-aarch64-linux-gnu)
|
|
87
87
|
racc (~> 1.4)
|
|
88
88
|
nokogiri (1.19.4-aarch64-linux-musl)
|
|
@@ -182,23 +182,23 @@ GEM
|
|
|
182
182
|
xsdvi (1.0.2)
|
|
183
183
|
nokogiri (~> 1.16)
|
|
184
184
|
thor (~> 1.3)
|
|
185
|
-
yeptris (0.6.
|
|
185
|
+
yeptris (0.6.36.0-aarch64-linux)
|
|
186
186
|
ffi (~> 1.15)
|
|
187
|
-
yeptris (0.6.
|
|
187
|
+
yeptris (0.6.36.0-aarch64-linux-musl)
|
|
188
188
|
ffi (~> 1.15)
|
|
189
|
-
yeptris (0.6.
|
|
189
|
+
yeptris (0.6.36.0-arm-linux-eabihf)
|
|
190
190
|
ffi (~> 1.15)
|
|
191
|
-
yeptris (0.6.
|
|
191
|
+
yeptris (0.6.36.0-arm-linux-musleabihf)
|
|
192
192
|
ffi (~> 1.15)
|
|
193
|
-
yeptris (0.6.
|
|
193
|
+
yeptris (0.6.36.0-arm64-darwin)
|
|
194
194
|
ffi (~> 1.15)
|
|
195
|
-
yeptris (0.6.
|
|
195
|
+
yeptris (0.6.36.0-x64-mingw-ucrt)
|
|
196
196
|
ffi (~> 1.15)
|
|
197
|
-
yeptris (0.6.
|
|
197
|
+
yeptris (0.6.36.0-x86_64-darwin)
|
|
198
198
|
ffi (~> 1.15)
|
|
199
|
-
yeptris (0.6.
|
|
199
|
+
yeptris (0.6.36.0-x86_64-linux)
|
|
200
200
|
ffi (~> 1.15)
|
|
201
|
-
yeptris (0.6.
|
|
201
|
+
yeptris (0.6.36.0-x86_64-linux-musl)
|
|
202
202
|
ffi (~> 1.15)
|
|
203
203
|
|
|
204
204
|
PLATFORMS
|
|
@@ -235,7 +235,7 @@ CHECKSUMS
|
|
|
235
235
|
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
|
|
236
236
|
bigdecimal (4.1.3) sha256=61ebe1e5e559bdc3cc6f2c0ee7f427321fc838f59611c294356eb04d6e21cf66
|
|
237
237
|
bundler (4.0.23) sha256=407ba8a1d928f412847f819aa5f8b6aef7c59fb51feb517112af8a165487287b
|
|
238
|
-
canon (0.3.
|
|
238
|
+
canon (0.3.110) sha256=4cec68aade805afb276f820adc5700feb0470bcbc75d97a70647ec9c338cedd4
|
|
239
239
|
concurrent-ruby (1.3.8) sha256=b2f1be836e968ccc78ccfce277ea79c72a88633f22306782c16ff23fb415d1e1
|
|
240
240
|
csv (3.3.6) sha256=aba61e7e507a66f03d45cb1f3c4b6359861c3504038b422962875dce099e4456
|
|
241
241
|
diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
|
|
@@ -251,22 +251,22 @@ CHECKSUMS
|
|
|
251
251
|
fiddle (1.1.8) sha256=7fa8ee3627271497f3add5503acdbc3f40b32f610fc1cf49634f083ef3f32eee
|
|
252
252
|
json (3.1.0) sha256=3ce7ccf8fde58960177a3a756c31f292a4c1e1dedee884547096a5b0604e3bd8
|
|
253
253
|
language_server-protocol (3.17.0.6) sha256=5ef2c0c138f8267e1bc631d3328347d354f96724b0af22f2c79516120443b7f0
|
|
254
|
-
leptris (1.9.
|
|
255
|
-
leptris (1.9.
|
|
256
|
-
leptris (1.9.
|
|
257
|
-
leptris (1.9.
|
|
258
|
-
leptris (1.9.
|
|
259
|
-
leptris (1.9.
|
|
260
|
-
leptris (1.9.
|
|
261
|
-
leptris (1.9.
|
|
262
|
-
leptris (1.9.
|
|
254
|
+
leptris (1.9.380.2-aarch64-linux) sha256=63065f9b6a54b1a52eea5040439f946b4f1712729d277ed62eddea96526e2188
|
|
255
|
+
leptris (1.9.380.2-aarch64-linux-musl) sha256=6fed41560b33cec5edfacc205c2211c4110b0c48bf7c772baa3acd49efcbd2cc
|
|
256
|
+
leptris (1.9.380.2-arm-linux) sha256=553c135d0c50a5533b1726232b8c6a64ed3d903a006c786204a092e54c61ba01
|
|
257
|
+
leptris (1.9.380.2-arm-linux-musl) sha256=1172fad58c5f8f0b34d7701a3624ac4c3500ce963696137e821c5588737f1fb3
|
|
258
|
+
leptris (1.9.380.2-arm64-darwin) sha256=d94d55a2865744dec2d2b5ab3661de77400d3ca48fb36573c8dc58c3cd92c68f
|
|
259
|
+
leptris (1.9.380.2-x64-mingw-ucrt) sha256=7439cb09b2d0d9d88f3bb68ed832d0429a0e5019be4214d0342027c656538919
|
|
260
|
+
leptris (1.9.380.2-x86_64-darwin) sha256=e68a4c82bebcc5a5383eb76e0280be97c3b7edbf6f1eabf02e817ac057b49cfa
|
|
261
|
+
leptris (1.9.380.2-x86_64-linux) sha256=5b84a6ddc9bc297426bd36c15f4d0d878081954f3ab317a89cfb9fe36e8bd696
|
|
262
|
+
leptris (1.9.380.2-x86_64-linux-musl) sha256=abff7493153f8fd2516c056006866bb7fd680ab4fb1b83e89d92eca127a82cdb
|
|
263
263
|
lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
|
|
264
264
|
liquid (5.14.0) sha256=e1020c2b2ee58c7a4d3bfdc7a331537bd27a783353748bff0bf27c6ac0c17065
|
|
265
|
-
lutaml-model (0.8.
|
|
265
|
+
lutaml-model (0.8.123) sha256=475f391371965f5baee2a9d018a7c9da8361c3774d3802749aa8805ee1253fde
|
|
266
266
|
lutaml-store (0.3.2) sha256=9c455dbab32b4c635c48a27213f6f56fda191731c4574eb6187184ba0588a672
|
|
267
|
-
lutaml-xsd (1.1
|
|
267
|
+
lutaml-xsd (1.2.1)
|
|
268
268
|
memo_wise (1.13.0) sha256=30220c38c4cef410849bc73553c58664dc2c91c6379e4a1df22aea02358b716b
|
|
269
|
-
moxml (0.5.
|
|
269
|
+
moxml (0.5.153) sha256=561c6df843edd4a3b7f4c6785f9cfd6fef61050df7f1113fdcd45d8495704fc2
|
|
270
270
|
nokogiri (1.19.4-aarch64-linux-gnu) sha256=1269fb644a6de405057a53dd5c762b1209b43ca7424f839454d3dbc677c31a8f
|
|
271
271
|
nokogiri (1.19.4-aarch64-linux-musl) sha256=35c65b9ce72b3bb03207bdbe7067915019dc18c1b9b59139684bd6690fdd01af
|
|
272
272
|
nokogiri (1.19.4-arm-linux-gnu) sha256=a301313e38bb065d68239e79734bcd6f56fb6efaacebde29e9abf2a4735340ca
|
|
@@ -313,15 +313,15 @@ CHECKSUMS
|
|
|
313
313
|
unicode-types (1.12.0) sha256=7e26a4352cecc2a50325a3793df834b732581ca9f25c5e29f46f3378934363f6
|
|
314
314
|
wisper (2.0.1) sha256=ce17bc5c3a166f241a2e6613848b025c8146fce2defba505920c1d1f3f88fae6
|
|
315
315
|
xsdvi (1.0.2) sha256=edf56a78f0fa25e4a5791d235ea8740cfb620eafa4abb79442b30160cf7292fc
|
|
316
|
-
yeptris (0.6.
|
|
317
|
-
yeptris (0.6.
|
|
318
|
-
yeptris (0.6.
|
|
319
|
-
yeptris (0.6.
|
|
320
|
-
yeptris (0.6.
|
|
321
|
-
yeptris (0.6.
|
|
322
|
-
yeptris (0.6.
|
|
323
|
-
yeptris (0.6.
|
|
324
|
-
yeptris (0.6.
|
|
316
|
+
yeptris (0.6.36.0-aarch64-linux) sha256=68d48f9cb4deab432175a3d336740dad58c14f2072bdf368d71122a435375828
|
|
317
|
+
yeptris (0.6.36.0-aarch64-linux-musl) sha256=5d074f3b3eb08cfd54d88bfbebfdc3c899fd8b882baf797759aded7794c3eac9
|
|
318
|
+
yeptris (0.6.36.0-arm-linux-eabihf) sha256=9f59f310251840527a75b4481b304c7d304a8eff346a8d7f3424277346174f9a
|
|
319
|
+
yeptris (0.6.36.0-arm-linux-musleabihf) sha256=966d0122b56c27a7889717fc1fe05c1bb2e1fa0eb10e9408a58c38f146514393
|
|
320
|
+
yeptris (0.6.36.0-arm64-darwin) sha256=70381795c9a8bb9d0264409a3d376fc67b61bb2057d0781517621297dccc8f40
|
|
321
|
+
yeptris (0.6.36.0-x64-mingw-ucrt) sha256=24a6cab2ac83ebb7d9f40a6281a7683fa837c9e260e99e413865fd49fa2068e1
|
|
322
|
+
yeptris (0.6.36.0-x86_64-darwin) sha256=dbc291383605b54b4755b43434cf0e69e5ec222d068a581ad9a7558aa7d7e64a
|
|
323
|
+
yeptris (0.6.36.0-x86_64-linux) sha256=5d88fa3cf4d69eec2411a6e724c39fbc3d2f4dc303e8462f260a0054248b6b6f
|
|
324
|
+
yeptris (0.6.36.0-x86_64-linux-musl) sha256=42cf7e2bcf74c5f141c42a253e94f76ffaf4d673ddd96c544ec12974d21953e4
|
|
325
325
|
|
|
326
326
|
BUNDLED WITH
|
|
327
327
|
4.0.23
|
data/TODO.cleanup.md
CHANGED
|
@@ -19,14 +19,24 @@ release-gated or need owner confirmation.
|
|
|
19
19
|
sequence: 7 choices / 1 group / 1 element — `sections` present via
|
|
20
20
|
bsi's override); gemspec pins `rng ~> 0.3.9` (released with the
|
|
21
21
|
include fixes + Moxml migration)
|
|
22
|
-
- [
|
|
23
|
-
owner confirmation
|
|
22
|
+
- [x] `lib/lutaml/xsd/schema_query_service.rb.bak` (untracked) — REMOVED
|
|
23
|
+
2026-10-22 with owner confirmation: byte-identical to commit
|
|
24
|
+
be55f18 on the apply-lutaml-store branch, which has since moved the
|
|
25
|
+
file forward; nothing unique was lost
|
|
24
26
|
- [ ] Prunable stale worktrees (`/private/tmp/fix/xsd`, `/private/tmp/xsd-main`)
|
|
25
27
|
— DEFERRED, owner to run `git worktree prune` after confirming the
|
|
26
28
|
branches hold nothing needed
|
|
27
29
|
- [ ] Dead-code sweep — DEFERRED, owner confirmation per file (nothing is
|
|
28
30
|
auto-deleted); the first candidate found in this run is
|
|
29
31
|
`RncToRngConverter#add_annotations` in rng (uncalled since 0.3.x)
|
|
32
|
+
- [x] respond_to? sweep — COMPLETE (2026-10-22): validation stack, SPA
|
|
33
|
+
serializer, JSON Schema exporter and type index (PR #121), then
|
|
34
|
+
`rng_to_xsd_converter.rb` (11 sites): class-keyed
|
|
35
|
+
PATTERN_KINDS_BY_CLASS from the rng 0.3.9 accessor matrices,
|
|
36
|
+
occurrence_assignable? for the min/max-occurs assignments
|
|
37
|
+
(Xsd::Attribute/AttributeGroup carry none), direct
|
|
38
|
+
documentation/name accessors. Gated by the converter corpus
|
|
39
|
+
(21 specs, 170 complex types)
|
|
30
40
|
- [ ] `Gemfile.lock` divergence guard for other lutaml gems — not needed
|
|
31
41
|
yet; adopt the same pattern when those repos start hitting the
|
|
32
42
|
fresh-resolve class of failure
|
data/TODO.conformance.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Goal: conformance is a number, not a feeling. Vendor the W3C XML Schema
|
|
4
4
|
test-suite categories, build the harness, publish the pass-rate, grow it.
|
|
5
5
|
|
|
6
|
-
## Baseline (2026-10-22, 20 cases / 42 checks)
|
|
6
|
+
## Baseline (2026-10-22, 20 cases / 42 checks, full pass)
|
|
7
7
|
|
|
8
8
|
Corpus: 20 case classes under `spec/conformance/cases/`, modelled on the
|
|
9
9
|
W3C XML Schema Test Suite category taxonomy — self-authored because the
|
|
@@ -12,34 +12,30 @@ can vendor a tarball later; provenance README goes with it).
|
|
|
12
12
|
|
|
13
13
|
| mode | pass-rate |
|
|
14
14
|
|---|---|
|
|
15
|
-
| engine (Moxml::XSD tier-1,
|
|
16
|
-
| pipeline (ValidationJob + wired rule engine) |
|
|
15
|
+
| engine (Moxml::XSD tier-1, leptris 1.9.380.2) | 38/42 (90.5%) |
|
|
16
|
+
| pipeline (ValidationJob + wired rule engine) | 42/42 (100.0%) |
|
|
17
17
|
|
|
18
|
-
The pipeline
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
Gap register (each failure is a tracked gap, not a test bug):
|
|
26
|
-
|
|
27
|
-
- `attributes-any` valid-known — tier-1 engine reports a qualified
|
|
28
|
-
global attribute as "not declared" under strict anyAttribute (engine
|
|
29
|
-
limitation; pipeline accepts it correctly)
|
|
30
|
-
- `identity-keykeyref` — dangling keyref not detected: implemented in
|
|
31
|
-
`IdentityConstraintsRule`, but activation needs lutaml-model to map the
|
|
32
|
-
`refer` attribute on `xs:keyref` (upstream fix in flight)
|
|
33
|
-
- `xsi-type` — a VALID instance derived via `xsi:type` is rejected by the
|
|
34
|
-
tier-1 engine (false positive, structural: engine scope is stricter than
|
|
35
|
-
derivation); the unknown-type half passes in pipeline mode
|
|
36
|
-
- engine-only: `namespaces-qualified` wrong-namespace root accepted
|
|
37
|
-
(EngineContext root-matching skips it; pipeline's root check catches it)
|
|
18
|
+
The pipeline now passes every check in the corpus. The engine trails on
|
|
19
|
+
four: strict wildcard/anyAttribute under-detection, xsi:nil on
|
|
20
|
+
non-nillable elements, and the wrong-namespace root (root-matching
|
|
21
|
+
skips it) — all documented tier-1 laxness; the pipeline enforces them.
|
|
22
|
+
The engine's former xsi:type derivation FALSE POSITIVE is fixed in
|
|
23
|
+
leptris 1.9.380.2 (moxml#433 closed).
|
|
38
24
|
|
|
39
25
|
History: 2026-10-20 baseline 20/25 (80.0%) with pipeline == engine
|
|
40
26
|
(empty `RuleRegistry`, mismatched rule signatures); 2026-10-21 wiring
|
|
41
|
-
baseline 34/39 (87.2%); gap rules 37/39 (94.9%); anyAttribute
|
|
42
|
-
|
|
27
|
+
baseline 34/39 (87.2%); gap rules 37/39 (94.9%); anyAttribute 40/42
|
|
28
|
+
(95.2%); keyref activation via lutaml-model 0.8.121 → 41/42 (97.6%);
|
|
29
|
+
lutaml-model 0.8.123 (complexType/extension anyAttribute mappings) +
|
|
30
|
+
leptris 1.9.380.2 → 42/42 (100.0%).
|
|
31
|
+
|
|
32
|
+
Remaining engine-mode gaps (tracked, moxml/leptris-side):
|
|
33
|
+
|
|
34
|
+
- strict `xs:any` and `xs:anyAttribute` under-detection
|
|
35
|
+
(`invalid-undeclared` instances validate) — documented lax posture
|
|
36
|
+
- `xsi:nil` on non-nillable elements accepted
|
|
37
|
+
- `namespaces-qualified`: wrong-namespace root accepted (engine
|
|
38
|
+
root-matching skips it; the pipeline's root check catches it)
|
|
43
39
|
|
|
44
40
|
## Harness
|
|
45
41
|
|
|
@@ -80,20 +76,26 @@ pipeline to 40/42 (95.2%).
|
|
|
80
76
|
|
|
81
77
|
- [x] Identity constraints: `key`, `keyref`, `unique` —
|
|
82
78
|
`IdentityConstraintsRule` (selector/field XPath evaluation,
|
|
83
|
-
duplicate detection, keyref resolution); dangling-keyref
|
|
84
|
-
|
|
79
|
+
duplicate detection, keyref resolution); dangling-keyref detection
|
|
80
|
+
active since lutaml-model 0.8.121 (lutaml-model#990)
|
|
85
81
|
- [x] Wildcards: `any` namespace constraints (##any/##other/##local/
|
|
86
82
|
##targetNamespace/lists) + `processContents` strict declaration
|
|
87
83
|
checking in the content-model walk; `anyAttribute` honors namespace
|
|
88
|
-
constraints and strict checking
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
behavior until mapped
|
|
84
|
+
constraints and strict checking on every shape — attributeGroup and
|
|
85
|
+
restriction always, complexType-level and extension shapes since
|
|
86
|
+
lutaml-model 0.8.123 maps them (lutaml-model#995); the capability
|
|
87
|
+
probe keeps older models on pre-wildcard behavior
|
|
93
88
|
- [x] `xsi:nil` (nillable + empty-content checks) and `xsi:type`
|
|
94
89
|
resolution (unknown-type rejection; content walk follows the
|
|
95
|
-
override); valid-derived
|
|
96
|
-
|
|
97
|
-
|
|
90
|
+
override); the engine's valid-derived false positive is fixed in
|
|
91
|
+
leptris 1.9.380.2 (moxml#433)
|
|
92
|
+
- [x] Particle checking: UPA detection (2026-10-22) —
|
|
93
|
+
`UniqueParticleAttribution` schema-side checker: first-set
|
|
94
|
+
computation over sequence/choice/all with wildcard/element
|
|
95
|
+
overlap, choice-alternative and sequence-junction conflict rules,
|
|
96
|
+
container occurrence folding. Standalone API
|
|
97
|
+
(`findings` → upa_violation findings); zero findings on the
|
|
98
|
+
conformance corpus schemas (no false positives). Deferred depth:
|
|
99
|
+
xs:group expansion, substitution-group-aware first-sets
|
|
98
100
|
- [ ] XSD 1.1 constructs behind a version flag — DEFERRED: 1.1 strategy
|
|
99
101
|
decision needed (see TODO.som.md)
|
|
@@ -13,6 +13,21 @@ module Lutaml
|
|
|
13
13
|
class JsonSchemaExporter
|
|
14
14
|
DRAFT = "https://json-schema.org/draft/2020-12/schema"
|
|
15
15
|
|
|
16
|
+
# Nested particle accessors per container class — declared model
|
|
17
|
+
# attributes on lutaml-model 0.8.114+ (xs:all cannot nest, and
|
|
18
|
+
# only complexType/extension/restriction carry one level of
|
|
19
|
+
# sequence/choice/all).
|
|
20
|
+
PARTICLE_KINDS_BY_CLASS = {
|
|
21
|
+
Lutaml::Xml::Schema::Xsd::ComplexType => %i[sequence choice all],
|
|
22
|
+
Lutaml::Xml::Schema::Xsd::ExtensionComplexContent =>
|
|
23
|
+
%i[sequence choice all],
|
|
24
|
+
Lutaml::Xml::Schema::Xsd::RestrictionComplexContent =>
|
|
25
|
+
%i[sequence choice all],
|
|
26
|
+
Lutaml::Xml::Schema::Xsd::Sequence => %i[sequence choice],
|
|
27
|
+
Lutaml::Xml::Schema::Xsd::Choice => %i[sequence choice],
|
|
28
|
+
Lutaml::Xml::Schema::Xsd::All => [].freeze,
|
|
29
|
+
}.freeze
|
|
30
|
+
|
|
16
31
|
BUILTIN_TYPES = {
|
|
17
32
|
"string" => { "type" => "string" },
|
|
18
33
|
"normalizedString" => { "type" => "string" },
|
|
@@ -157,18 +172,30 @@ module Lutaml
|
|
|
157
172
|
def each_element_particle(node, &)
|
|
158
173
|
return unless node
|
|
159
174
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
175
|
+
kinds = PARTICLE_KINDS_BY_CLASS[node.class]
|
|
176
|
+
return unless kinds
|
|
177
|
+
|
|
178
|
+
kinds.each do |kind|
|
|
179
|
+
Array(node.public_send(kind)).each do |particle|
|
|
180
|
+
next unless particle
|
|
163
181
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
182
|
+
Array(particle.element).each(&)
|
|
183
|
+
Array(particle.choice).each do |choice|
|
|
184
|
+
each_element_particle(choice, &)
|
|
185
|
+
end
|
|
186
|
+
Array(particle.sequence).each do |seq|
|
|
187
|
+
each_element_particle(seq, &)
|
|
188
|
+
end
|
|
189
|
+
end
|
|
167
190
|
end
|
|
168
|
-
|
|
191
|
+
|
|
192
|
+
return unless node.is_a?(Lutaml::Xml::Schema::Xsd::ComplexType)
|
|
193
|
+
|
|
194
|
+
content = node.complex_content
|
|
195
|
+
return unless content
|
|
169
196
|
|
|
170
197
|
%i[extension restriction].each do |kind|
|
|
171
|
-
part =
|
|
198
|
+
part = content.public_send(kind)
|
|
172
199
|
each_element_particle(part, &) if part
|
|
173
200
|
end
|
|
174
201
|
end
|
|
@@ -51,8 +51,44 @@ module Lutaml
|
|
|
51
51
|
schema
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
+
# Nested pattern accessors per container class — declared model
|
|
55
|
+
# attributes on rng 0.3.9 (Grammar carries only element; Start/List
|
|
56
|
+
# and Attribute omit one kind each; the other containers declare
|
|
57
|
+
# the full set).
|
|
58
|
+
FULL_PATTERN_KINDS = %w[element ref choice group interleave mixed
|
|
59
|
+
optional zeroOrMore oneOrMore text empty
|
|
60
|
+
value data list notAllowed attribute].freeze
|
|
61
|
+
PATTERN_KINDS_BY_CLASS = {
|
|
62
|
+
"Rng::Element" => FULL_PATTERN_KINDS,
|
|
63
|
+
"Rng::Choice" => FULL_PATTERN_KINDS,
|
|
64
|
+
"Rng::Group" => FULL_PATTERN_KINDS,
|
|
65
|
+
"Rng::Interleave" => FULL_PATTERN_KINDS,
|
|
66
|
+
"Rng::Mixed" => FULL_PATTERN_KINDS,
|
|
67
|
+
"Rng::Optional" => FULL_PATTERN_KINDS,
|
|
68
|
+
"Rng::ZeroOrMore" => FULL_PATTERN_KINDS,
|
|
69
|
+
"Rng::OneOrMore" => FULL_PATTERN_KINDS,
|
|
70
|
+
"Rng::Define" => FULL_PATTERN_KINDS,
|
|
71
|
+
"Rng::Attribute" =>
|
|
72
|
+
%w[ref choice group interleave mixed optional zeroOrMore
|
|
73
|
+
oneOrMore text empty value data list notAllowed attribute],
|
|
74
|
+
"Rng::Start" =>
|
|
75
|
+
%w[element ref choice group interleave mixed optional zeroOrMore
|
|
76
|
+
oneOrMore text empty value data list notAllowed],
|
|
77
|
+
"Rng::List" =>
|
|
78
|
+
%w[element ref choice group interleave mixed optional zeroOrMore
|
|
79
|
+
oneOrMore text empty value data notAllowed],
|
|
80
|
+
"Rng::Grammar" => %w[element],
|
|
81
|
+
}.freeze
|
|
82
|
+
|
|
54
83
|
private
|
|
55
84
|
|
|
85
|
+
# Occurrence attributes exist on particle models (element,
|
|
86
|
+
# containers); attribute declarations and attribute groups carry none.
|
|
87
|
+
def occurrence_assignable?(node)
|
|
88
|
+
!node.is_a?(Lutaml::Xml::Schema::Xsd::Attribute) &&
|
|
89
|
+
!node.is_a?(Lutaml::Xml::Schema::Xsd::AttributeGroup)
|
|
90
|
+
end
|
|
91
|
+
|
|
56
92
|
# Resolve a value that might be Lutaml::Model::UninitializedClass to nil or a string
|
|
57
93
|
def resolve_string(value)
|
|
58
94
|
return nil if value.nil? || value.is_a?(Lutaml::Model::UninitializedClass)
|
|
@@ -63,27 +99,20 @@ module Lutaml
|
|
|
63
99
|
# Collect all pattern children from an RNG container node
|
|
64
100
|
# Returns an array of non-nil pattern objects
|
|
65
101
|
def get_all_patterns(container)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
children = container.send(attr)
|
|
71
|
-
next if children.nil?
|
|
72
|
-
next if children.is_a?(Lutaml::Model::UninitializedClass)
|
|
73
|
-
|
|
74
|
-
if children.is_a?(Array)
|
|
75
|
-
patterns.concat(children.compact)
|
|
76
|
-
else
|
|
77
|
-
patterns << children
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
patterns
|
|
102
|
+
kinds = PATTERN_KINDS_BY_CLASS[container.class.name] || []
|
|
103
|
+
kinds.flat_map { |attr| Array(container.public_send(attr)) }
|
|
104
|
+
.compact
|
|
105
|
+
.reject { |child| child.is_a?(Lutaml::Model::UninitializedClass) }
|
|
81
106
|
end
|
|
82
107
|
|
|
83
|
-
#
|
|
84
|
-
def
|
|
85
|
-
|
|
86
|
-
|
|
108
|
+
# Get the single pattern child from a Start
|
|
109
|
+
def get_start_pattern(start)
|
|
110
|
+
kinds = PATTERN_KINDS_BY_CLASS[start.class.name] || []
|
|
111
|
+
kinds.map { |attr| start.public_send(attr) }
|
|
112
|
+
.find do |child|
|
|
113
|
+
child && !child.is_a?(Array) &&
|
|
114
|
+
!child.is_a?(Lutaml::Model::UninitializedClass)
|
|
115
|
+
end
|
|
87
116
|
end
|
|
88
117
|
|
|
89
118
|
# Build define name -> Define lookup from grammar (including divs)
|
|
@@ -925,17 +954,6 @@ module Lutaml
|
|
|
925
954
|
end
|
|
926
955
|
end
|
|
927
956
|
|
|
928
|
-
# Get the single pattern child from a Start
|
|
929
|
-
def get_start_pattern(start)
|
|
930
|
-
pattern_types.each do |attr|
|
|
931
|
-
next unless start.respond_to?(attr)
|
|
932
|
-
|
|
933
|
-
child = start.send(attr)
|
|
934
|
-
return child if child && !child.is_a?(Array) && !child.is_a?(Lutaml::Model::UninitializedClass)
|
|
935
|
-
end
|
|
936
|
-
nil
|
|
937
|
-
end
|
|
938
|
-
|
|
939
957
|
# Phase 2b: Convert top-level elements from grammar
|
|
940
958
|
def convert_grammar_elements(schema)
|
|
941
959
|
(@grammar.element || []).each do |rng_elem|
|
|
@@ -1034,7 +1052,7 @@ module Lutaml
|
|
|
1034
1052
|
xsd_elem = Lutaml::Xml::Schema::Xsd::Element.new(name: name)
|
|
1035
1053
|
|
|
1036
1054
|
# Add documentation if present
|
|
1037
|
-
if rng_elem.
|
|
1055
|
+
if rng_elem.documentation
|
|
1038
1056
|
xsd_elem.annotation = Lutaml::Xml::Schema::Xsd::Annotation.new(
|
|
1039
1057
|
documentation: [
|
|
1040
1058
|
Lutaml::Xml::Schema::Xsd::Documentation.new(
|
|
@@ -1134,7 +1152,7 @@ module Lutaml
|
|
|
1134
1152
|
xsd_attr.type = "xs:string"
|
|
1135
1153
|
end
|
|
1136
1154
|
|
|
1137
|
-
if rng_attr.
|
|
1155
|
+
if rng_attr.documentation
|
|
1138
1156
|
xsd_attr.annotation = Lutaml::Xml::Schema::Xsd::Annotation.new(
|
|
1139
1157
|
documentation: [
|
|
1140
1158
|
Lutaml::Xml::Schema::Xsd::Documentation.new(
|
|
@@ -1149,21 +1167,23 @@ module Lutaml
|
|
|
1149
1167
|
|
|
1150
1168
|
# Get the single pattern child from an Attribute
|
|
1151
1169
|
def get_attribute_child(attr)
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
nil
|
|
1170
|
+
kinds = PATTERN_KINDS_BY_CLASS[attr.class.name] || []
|
|
1171
|
+
kinds.map { |type_name| attr.public_send(type_name) }
|
|
1172
|
+
.find do |child|
|
|
1173
|
+
child && !child.is_a?(Array) &&
|
|
1174
|
+
!child.is_a?(Lutaml::Model::UninitializedClass)
|
|
1175
|
+
end
|
|
1159
1176
|
end
|
|
1160
1177
|
|
|
1161
|
-
# Extract element/attribute name from attr_name or name.value
|
|
1178
|
+
# Extract element/attribute name from attr_name or name.value;
|
|
1179
|
+
# only Rng::Element and Rng::Attribute carry these accessors.
|
|
1162
1180
|
def element_name(node)
|
|
1163
|
-
|
|
1164
|
-
|
|
1181
|
+
return nil unless node.is_a?(Rng::Element) || node.is_a?(Rng::Attribute)
|
|
1182
|
+
|
|
1183
|
+
name = node.attr_name
|
|
1184
|
+
if name.nil? || name.empty?
|
|
1165
1185
|
name_val = node.name
|
|
1166
|
-
name = name_val.value if name_val.
|
|
1186
|
+
name = name_val.value if name_val.is_a?(Rng::Name)
|
|
1167
1187
|
end
|
|
1168
1188
|
name
|
|
1169
1189
|
end
|
|
@@ -1226,7 +1246,8 @@ module Lutaml
|
|
|
1226
1246
|
end
|
|
1227
1247
|
|
|
1228
1248
|
# Per jing-trang: if any non-element child, wrap in optional
|
|
1229
|
-
|
|
1249
|
+
# (attribute groups carry no occurrence attributes)
|
|
1250
|
+
if has_non_element && occurrence_assignable?(result)
|
|
1230
1251
|
result.min_occurs = "0"
|
|
1231
1252
|
result.max_occurs = "1"
|
|
1232
1253
|
end
|
|
@@ -1279,7 +1300,7 @@ module Lutaml
|
|
|
1279
1300
|
|
|
1280
1301
|
if xsd_children.size == 1
|
|
1281
1302
|
child = xsd_children.first
|
|
1282
|
-
if
|
|
1303
|
+
if occurrence_assignable?(child)
|
|
1283
1304
|
child.min_occurs = min
|
|
1284
1305
|
child.max_occurs = max
|
|
1285
1306
|
end
|
|
@@ -1329,7 +1350,7 @@ module Lutaml
|
|
|
1329
1350
|
# Reference to a define promoted to top-level element -> element ref
|
|
1330
1351
|
# Use the element's actual name (not the define name) as the ref target,
|
|
1331
1352
|
# since they may differ (e.g., define "ext_toc" wraps element "name").
|
|
1332
|
-
elem_name = result[:element].
|
|
1353
|
+
elem_name = result[:element].name
|
|
1333
1354
|
Lutaml::Xml::Schema::Xsd::Element.new(ref: elem_name)
|
|
1334
1355
|
elsif result[:group]
|
|
1335
1356
|
Lutaml::Xml::Schema::Xsd::Group.new(ref: name)
|
|
@@ -61,11 +61,11 @@ module Lutaml
|
|
|
61
61
|
# @param namespace [String] The namespace URI
|
|
62
62
|
# @param file_path [String] Path to the schema file
|
|
63
63
|
def index_inline_complex_types(schema, namespace, file_path)
|
|
64
|
-
return unless schema.
|
|
64
|
+
return unless schema.element
|
|
65
65
|
|
|
66
66
|
elements = schema.element.is_a?(Array) ? schema.element : [schema.element]
|
|
67
67
|
elements.compact.each do |element|
|
|
68
|
-
next unless element.
|
|
68
|
+
next unless element.complex_type
|
|
69
69
|
|
|
70
70
|
inline_ct = element.complex_type
|
|
71
71
|
# Only index if it doesn't already have a name (truly anonymous)
|