avrotize 2.20.3__py3-none-any.whl → 2.20.4__py3-none-any.whl
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.
- avrotize/__init__.py +2 -0
- avrotize/_version.py +2 -2
- avrotize/avrotocsharp/README.md.jinja +166 -0
- avrotize/avrotocsharp/class_test.cs.jinja +126 -0
- avrotize/avrotocsharp/dataclass_core.jinja +67 -5
- avrotize/avrotocsharp/project.csproj.jinja +6 -0
- avrotize/avrotocsharp/run_coverage.ps1.jinja +98 -0
- avrotize/avrotocsharp/run_coverage.sh.jinja +149 -0
- avrotize/avrotocsharp/testproject.csproj.jinja +4 -0
- avrotize/avrotocsharp.py +121 -16
- avrotize/commands.json +168 -9
- avrotize/constants.py +15 -0
- avrotize/dependencies/cs/net90/dependencies.csproj +2 -0
- avrotize/dependencies/java/jdk21/pom.xml +1 -1
- avrotize/jsonstostructure.py +234 -12
- avrotize/openapitostructure.py +717 -0
- avrotize/structuretojs/class_core.js.jinja +33 -0
- avrotize/structuretojs/enum_core.js.jinja +10 -0
- avrotize/structuretojs/package.json.jinja +12 -0
- avrotize/structuretojs/test_class.js.jinja +84 -0
- avrotize/structuretojs/test_enum.js.jinja +58 -0
- avrotize/structuretojs/test_runner.js.jinja +45 -0
- avrotize/structuretojs.py +657 -0
- avrotize/structuretots.py +26 -2
- {avrotize-2.20.3.dist-info → avrotize-2.20.4.dist-info}/METADATA +66 -1
- {avrotize-2.20.3.dist-info → avrotize-2.20.4.dist-info}/RECORD +29 -18
- {avrotize-2.20.3.dist-info → avrotize-2.20.4.dist-info}/WHEEL +0 -0
- {avrotize-2.20.3.dist-info → avrotize-2.20.4.dist-info}/entry_points.txt +0 -0
- {avrotize-2.20.3.dist-info → avrotize-2.20.4.dist-info}/licenses/LICENSE +0 -0
avrotize/commands.json
CHANGED
|
@@ -1070,7 +1070,10 @@
|
|
|
1070
1070
|
"emit_cloudevents_columns": "args.emit_cloudevents_columns"
|
|
1071
1071
|
}
|
|
1072
1072
|
},
|
|
1073
|
-
"extensions": [
|
|
1073
|
+
"extensions": [
|
|
1074
|
+
".struct.json",
|
|
1075
|
+
".json"
|
|
1076
|
+
],
|
|
1074
1077
|
"args": [
|
|
1075
1078
|
{
|
|
1076
1079
|
"name": "input",
|
|
@@ -1248,6 +1251,7 @@
|
|
|
1248
1251
|
"avro_schema_path": "input_file_path",
|
|
1249
1252
|
"cs_file_path": "output_file_path",
|
|
1250
1253
|
"avro_annotation": "args.avro_annotation",
|
|
1254
|
+
"protobuf_net_annotation": "args.protobuf_net_annotation",
|
|
1251
1255
|
"system_text_json_annotation": "args.system_text_json_annotation",
|
|
1252
1256
|
"newtonsoft_json_annotation": "args.newtonsoft_json_annotation",
|
|
1253
1257
|
"system_xml_annotation": "args.system_xml_annotation",
|
|
@@ -1291,6 +1295,13 @@
|
|
|
1291
1295
|
"default": false,
|
|
1292
1296
|
"required": false
|
|
1293
1297
|
},
|
|
1298
|
+
{
|
|
1299
|
+
"name": "--protobuf-net-annotation",
|
|
1300
|
+
"type": "bool",
|
|
1301
|
+
"help": "Use protobuf-net annotations",
|
|
1302
|
+
"default": false,
|
|
1303
|
+
"required": false
|
|
1304
|
+
},
|
|
1294
1305
|
{
|
|
1295
1306
|
"name": "--system_text_json_annotation",
|
|
1296
1307
|
"type": "bool",
|
|
@@ -1704,7 +1715,10 @@
|
|
|
1704
1715
|
"json_annotation": "args.json_annotation"
|
|
1705
1716
|
}
|
|
1706
1717
|
},
|
|
1707
|
-
"extensions": [
|
|
1718
|
+
"extensions": [
|
|
1719
|
+
".struct.json",
|
|
1720
|
+
".json"
|
|
1721
|
+
],
|
|
1708
1722
|
"args": [
|
|
1709
1723
|
{
|
|
1710
1724
|
"name": "input",
|
|
@@ -1754,7 +1768,10 @@
|
|
|
1754
1768
|
"csv_schema_path": "output_file_path"
|
|
1755
1769
|
}
|
|
1756
1770
|
},
|
|
1757
|
-
"extensions": [
|
|
1771
|
+
"extensions": [
|
|
1772
|
+
".struct.json",
|
|
1773
|
+
".json"
|
|
1774
|
+
],
|
|
1758
1775
|
"args": [
|
|
1759
1776
|
{
|
|
1760
1777
|
"name": "input",
|
|
@@ -1786,7 +1803,10 @@
|
|
|
1786
1803
|
"serde_annotation": "args.json_annotation"
|
|
1787
1804
|
}
|
|
1788
1805
|
},
|
|
1789
|
-
"extensions": [
|
|
1806
|
+
"extensions": [
|
|
1807
|
+
".struct.json",
|
|
1808
|
+
".json"
|
|
1809
|
+
],
|
|
1790
1810
|
"args": [
|
|
1791
1811
|
{
|
|
1792
1812
|
"name": "input",
|
|
@@ -1845,7 +1865,10 @@
|
|
|
1845
1865
|
"avro_annotation": "args.avro_annotation"
|
|
1846
1866
|
}
|
|
1847
1867
|
},
|
|
1848
|
-
"extensions": [
|
|
1868
|
+
"extensions": [
|
|
1869
|
+
".struct.json",
|
|
1870
|
+
".json"
|
|
1871
|
+
],
|
|
1849
1872
|
"args": [
|
|
1850
1873
|
{
|
|
1851
1874
|
"name": "input",
|
|
@@ -1905,7 +1928,10 @@
|
|
|
1905
1928
|
"jackson_annotation": "args.jackson_annotation"
|
|
1906
1929
|
}
|
|
1907
1930
|
},
|
|
1908
|
-
"extensions": [
|
|
1931
|
+
"extensions": [
|
|
1932
|
+
".struct.json",
|
|
1933
|
+
".json"
|
|
1934
|
+
],
|
|
1909
1935
|
"args": [
|
|
1910
1936
|
{
|
|
1911
1937
|
"name": "input",
|
|
@@ -1958,6 +1984,51 @@
|
|
|
1958
1984
|
}
|
|
1959
1985
|
]
|
|
1960
1986
|
},
|
|
1987
|
+
{
|
|
1988
|
+
"command": "s2js",
|
|
1989
|
+
"description": "Convert JSON Structure to JavaScript classes",
|
|
1990
|
+
"group": "2_ProgLanguages",
|
|
1991
|
+
"function": {
|
|
1992
|
+
"name": "avrotize.structuretojs.convert_structure_to_javascript",
|
|
1993
|
+
"args": {
|
|
1994
|
+
"structure_schema_path": "input_file_path",
|
|
1995
|
+
"js_file_path": "output_file_path",
|
|
1996
|
+
"package_name": "args.package",
|
|
1997
|
+
"avro_annotation": "args.avro_annotation"
|
|
1998
|
+
}
|
|
1999
|
+
},
|
|
2000
|
+
"extensions": [".struct.json", ".json"],
|
|
2001
|
+
"args": [
|
|
2002
|
+
{
|
|
2003
|
+
"name": "input",
|
|
2004
|
+
"type": "str",
|
|
2005
|
+
"nargs": "?",
|
|
2006
|
+
"help": "Path to the JSON Structure schema file (or read from stdin if omitted)",
|
|
2007
|
+
"required": false
|
|
2008
|
+
},
|
|
2009
|
+
{
|
|
2010
|
+
"name": "--out",
|
|
2011
|
+
"type": "str",
|
|
2012
|
+
"help": "Output path for the JavaScript classes",
|
|
2013
|
+
"required": true
|
|
2014
|
+
},
|
|
2015
|
+
{
|
|
2016
|
+
"name": "--package",
|
|
2017
|
+
"type": "str",
|
|
2018
|
+
"help": "JavaScript package name",
|
|
2019
|
+
"required": false
|
|
2020
|
+
},
|
|
2021
|
+
{
|
|
2022
|
+
"name": "--avro-annotation",
|
|
2023
|
+
"type": "bool",
|
|
2024
|
+
"help": "Add Avro binary serialization support",
|
|
2025
|
+
"default": false,
|
|
2026
|
+
"required": false
|
|
2027
|
+
}
|
|
2028
|
+
],
|
|
2029
|
+
"suggested_output_file_path": "{input_file_name}-js",
|
|
2030
|
+
"prompts": []
|
|
2031
|
+
},
|
|
1961
2032
|
{
|
|
1962
2033
|
"command": "s2go",
|
|
1963
2034
|
"description": "Convert JSON Structure to Go structs",
|
|
@@ -1974,7 +2045,10 @@
|
|
|
1974
2045
|
"package_username": "args.package_username"
|
|
1975
2046
|
}
|
|
1976
2047
|
},
|
|
1977
|
-
"extensions": [
|
|
2048
|
+
"extensions": [
|
|
2049
|
+
".struct.json",
|
|
2050
|
+
".json"
|
|
2051
|
+
],
|
|
1978
2052
|
"args": [
|
|
1979
2053
|
{
|
|
1980
2054
|
"name": "input",
|
|
@@ -3195,7 +3269,10 @@
|
|
|
3195
3269
|
"target_namespace": "args.namespace"
|
|
3196
3270
|
}
|
|
3197
3271
|
},
|
|
3198
|
-
"extensions": [
|
|
3272
|
+
"extensions": [
|
|
3273
|
+
".struct.json",
|
|
3274
|
+
".json"
|
|
3275
|
+
],
|
|
3199
3276
|
"args": [
|
|
3200
3277
|
{
|
|
3201
3278
|
"name": "input",
|
|
@@ -3237,7 +3314,10 @@
|
|
|
3237
3314
|
"cddl_file_path": "output_file_path"
|
|
3238
3315
|
}
|
|
3239
3316
|
},
|
|
3240
|
-
"extensions": [
|
|
3317
|
+
"extensions": [
|
|
3318
|
+
".struct.json",
|
|
3319
|
+
".json"
|
|
3320
|
+
],
|
|
3241
3321
|
"args": [
|
|
3242
3322
|
{
|
|
3243
3323
|
"name": "input",
|
|
@@ -3333,5 +3413,84 @@
|
|
|
3333
3413
|
"required": false
|
|
3334
3414
|
}
|
|
3335
3415
|
]
|
|
3416
|
+
},
|
|
3417
|
+
{
|
|
3418
|
+
"command": "oas2s",
|
|
3419
|
+
"description": "Convert OpenAPI 3.x document to JSON Structure",
|
|
3420
|
+
"group": "1_Schemas",
|
|
3421
|
+
"function": {
|
|
3422
|
+
"name": "avrotize.openapitostructure.convert_openapi_to_structure_files",
|
|
3423
|
+
"args": {
|
|
3424
|
+
"openapi_file_path": "input_file_path",
|
|
3425
|
+
"structure_schema_path": "output_file_path",
|
|
3426
|
+
"root_namespace": "args.namespace",
|
|
3427
|
+
"preserve_composition": "args.preserve_composition",
|
|
3428
|
+
"detect_discriminators": "args.detect_discriminators",
|
|
3429
|
+
"lift_inline_schemas": "args.lift_inline_schemas"
|
|
3430
|
+
}
|
|
3431
|
+
},
|
|
3432
|
+
"extensions": [
|
|
3433
|
+
".yaml",
|
|
3434
|
+
".yml",
|
|
3435
|
+
".json"
|
|
3436
|
+
],
|
|
3437
|
+
"args": [
|
|
3438
|
+
{
|
|
3439
|
+
"name": "input",
|
|
3440
|
+
"type": "str",
|
|
3441
|
+
"nargs": "?",
|
|
3442
|
+
"help": "Path to the OpenAPI document (or read from stdin if omitted)",
|
|
3443
|
+
"required": false
|
|
3444
|
+
},
|
|
3445
|
+
{
|
|
3446
|
+
"name": "--out",
|
|
3447
|
+
"type": "str",
|
|
3448
|
+
"help": "Path to the JSON Structure output file",
|
|
3449
|
+
"required": false
|
|
3450
|
+
},
|
|
3451
|
+
{
|
|
3452
|
+
"name": "--namespace",
|
|
3453
|
+
"type": "str",
|
|
3454
|
+
"help": "Namespace for the JSON Structure schema",
|
|
3455
|
+
"required": false
|
|
3456
|
+
},
|
|
3457
|
+
{
|
|
3458
|
+
"name": "--preserve-composition",
|
|
3459
|
+
"type": "bool",
|
|
3460
|
+
"help": "Preserve composition keywords (allOf, oneOf, anyOf) - requires Conditional Composition extension",
|
|
3461
|
+
"default": false,
|
|
3462
|
+
"required": false
|
|
3463
|
+
},
|
|
3464
|
+
{
|
|
3465
|
+
"name": "--detect-discriminators",
|
|
3466
|
+
"type": "bool",
|
|
3467
|
+
"help": "Detect OpenAPI discriminator patterns",
|
|
3468
|
+
"default": true,
|
|
3469
|
+
"required": false
|
|
3470
|
+
},
|
|
3471
|
+
{
|
|
3472
|
+
"name": "--lift-inline-schemas",
|
|
3473
|
+
"type": "bool",
|
|
3474
|
+
"help": "Lift inline schemas from paths to named definitions",
|
|
3475
|
+
"default": false,
|
|
3476
|
+
"required": false
|
|
3477
|
+
}
|
|
3478
|
+
],
|
|
3479
|
+
"suggested_output_file_path": "{input_file_name}.struct.json",
|
|
3480
|
+
"prompts": [
|
|
3481
|
+
{
|
|
3482
|
+
"name": "--namespace",
|
|
3483
|
+
"message": "Enter the namespace for the JSON Structure schema",
|
|
3484
|
+
"type": "str",
|
|
3485
|
+
"required": false
|
|
3486
|
+
},
|
|
3487
|
+
{
|
|
3488
|
+
"name": "--lift-inline-schemas",
|
|
3489
|
+
"message": "Lift inline schemas from paths to definitions?",
|
|
3490
|
+
"type": "bool",
|
|
3491
|
+
"default": false,
|
|
3492
|
+
"required": false
|
|
3493
|
+
}
|
|
3494
|
+
]
|
|
3336
3495
|
}
|
|
3337
3496
|
]
|
avrotize/constants.py
CHANGED
|
@@ -40,6 +40,11 @@ try:
|
|
|
40
40
|
except ValueError:
|
|
41
41
|
SYSTEM_MEMORY_DATA_VERSION = '9.0.3'
|
|
42
42
|
|
|
43
|
+
try:
|
|
44
|
+
PROTOBUF_NET_VERSION = get_dependency_version('cs', 'net90', 'protobuf-net')
|
|
45
|
+
except ValueError:
|
|
46
|
+
PROTOBUF_NET_VERSION = '3.2.30'
|
|
47
|
+
|
|
43
48
|
try:
|
|
44
49
|
NUNIT_VERSION = get_dependency_version('cs', 'net90', 'NUnit')
|
|
45
50
|
except ValueError:
|
|
@@ -55,6 +60,16 @@ try:
|
|
|
55
60
|
except ValueError:
|
|
56
61
|
MSTEST_SDK_VERSION = '17.13.0'
|
|
57
62
|
|
|
63
|
+
try:
|
|
64
|
+
COVERLET_VERSION = get_dependency_version('cs', 'net90', 'coverlet.collector')
|
|
65
|
+
except ValueError:
|
|
66
|
+
COVERLET_VERSION = '6.0.4'
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
MSGPACK_VERSION = get_dependency_version('cs', 'net90', 'MessagePack')
|
|
70
|
+
except ValueError:
|
|
71
|
+
MSGPACK_VERSION = '2.5.187'
|
|
72
|
+
|
|
58
73
|
# Java test dependencies
|
|
59
74
|
try:
|
|
60
75
|
JUNIT_VERSION = get_dependency_version('java', 'jdk21', 'org.junit.jupiter:junit-jupiter-api')
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
|
17
17
|
<PackageReference Include="System.Text.Json" Version="9.0.3" />
|
|
18
18
|
<PackageReference Include="System.Memory.Data" Version="9.0.3" />
|
|
19
|
+
<PackageReference Include="protobuf-net" Version="3.2.30" />
|
|
20
|
+
<PackageReference Include="MessagePack" Version="3.1.4" />
|
|
19
21
|
</ItemGroup>
|
|
20
22
|
|
|
21
23
|
<!-- Testing -->
|
avrotize/jsonstostructure.py
CHANGED
|
@@ -388,10 +388,10 @@ class JsonToStructureConverter:
|
|
|
388
388
|
structure_type = 'string'
|
|
389
389
|
|
|
390
390
|
elif json_primitive == 'integer':
|
|
391
|
-
structure_type = self.detect_numeric_type({'type': 'integer', 'format': format
|
|
391
|
+
structure_type = self.detect_numeric_type({**schema, 'type': 'integer', 'format': format})
|
|
392
392
|
|
|
393
393
|
elif json_primitive == 'number':
|
|
394
|
-
structure_type = self.detect_numeric_type({'type': 'number', 'format': format
|
|
394
|
+
structure_type = self.detect_numeric_type({**schema, 'type': 'number', 'format': format})
|
|
395
395
|
|
|
396
396
|
elif json_primitive == 'boolean':
|
|
397
397
|
structure_type = 'boolean'
|
|
@@ -773,21 +773,38 @@ class JsonToStructureConverter:
|
|
|
773
773
|
Returns:
|
|
774
774
|
dict: JSON Structure choice definition
|
|
775
775
|
"""
|
|
776
|
+
# Handle both 'property' and 'propertyName' keys for compatibility
|
|
777
|
+
discriminator_property = discriminator_info.get('property') or discriminator_info.get('propertyName')
|
|
778
|
+
mapping = discriminator_info.get('mapping', {})
|
|
779
|
+
|
|
776
780
|
choice_obj = {
|
|
777
781
|
'type': 'choice',
|
|
778
|
-
'
|
|
782
|
+
'selector': discriminator_property,
|
|
779
783
|
'choices': {}
|
|
780
784
|
}
|
|
781
785
|
|
|
782
786
|
if record_name:
|
|
783
787
|
choice_obj['name'] = avro_name(record_name)
|
|
788
|
+
|
|
789
|
+
# Build reverse mapping from $ref to choice key
|
|
790
|
+
ref_to_key = {}
|
|
791
|
+
for key, ref in mapping.items():
|
|
792
|
+
ref_to_key[ref] = key
|
|
784
793
|
|
|
785
794
|
# Process each choice option
|
|
786
795
|
for i, option in enumerate(oneof_options):
|
|
787
796
|
if '$ref' in option:
|
|
788
|
-
# Handle reference
|
|
789
|
-
|
|
790
|
-
|
|
797
|
+
# Handle reference - use mapping to get the choice key
|
|
798
|
+
ref = option['$ref']
|
|
799
|
+
if ref in ref_to_key:
|
|
800
|
+
choice_key = ref_to_key[ref]
|
|
801
|
+
else:
|
|
802
|
+
# Extract name from reference
|
|
803
|
+
if ref.startswith('#/definitions/'):
|
|
804
|
+
choice_key = ref[14:] # Remove '#/definitions/' prefix
|
|
805
|
+
else:
|
|
806
|
+
choice_key = f"option_{i}"
|
|
807
|
+
choice_obj['choices'][choice_key] = {'$ref': ref}
|
|
791
808
|
else:
|
|
792
809
|
# Convert option to structure type
|
|
793
810
|
choice_key = f"option_{i}"
|
|
@@ -908,6 +925,77 @@ class JsonToStructureConverter:
|
|
|
908
925
|
'items': items_type
|
|
909
926
|
}
|
|
910
927
|
|
|
928
|
+
def _process_array_type(self, json_type: dict, record_name: str, field_name: str, namespace: str,
|
|
929
|
+
dependencies: list, json_schema: dict, base_uri: str,
|
|
930
|
+
structure_schema: dict, record_stack: list, recursion_depth: int) -> dict:
|
|
931
|
+
"""
|
|
932
|
+
Process an array type schema into JSON Structure format.
|
|
933
|
+
|
|
934
|
+
Args:
|
|
935
|
+
json_type: The JSON Schema with type: "array"
|
|
936
|
+
record_name: Name of the containing record
|
|
937
|
+
field_name: Name of the field
|
|
938
|
+
namespace: Namespace
|
|
939
|
+
dependencies: Dependencies list
|
|
940
|
+
json_schema: Full JSON schema
|
|
941
|
+
base_uri: Base URI
|
|
942
|
+
structure_schema: Structure schema being built
|
|
943
|
+
record_stack: Record stack for recursion detection
|
|
944
|
+
recursion_depth: Current recursion depth
|
|
945
|
+
|
|
946
|
+
Returns:
|
|
947
|
+
dict: JSON Structure array definition
|
|
948
|
+
"""
|
|
949
|
+
items_schema = json_type.get('items', {})
|
|
950
|
+
is_set = json_type.get('uniqueItems', False)
|
|
951
|
+
return self.create_structure_array_or_set(
|
|
952
|
+
items_schema, is_set, record_name, namespace, dependencies,
|
|
953
|
+
json_schema, base_uri, structure_schema, record_stack, recursion_depth + 1
|
|
954
|
+
)
|
|
955
|
+
|
|
956
|
+
def _process_object_type(self, json_type: dict, record_name: str, field_name: str, namespace: str,
|
|
957
|
+
dependencies: list, json_schema: dict, base_uri: str,
|
|
958
|
+
structure_schema: dict, record_stack: list, recursion_depth: int) -> dict:
|
|
959
|
+
"""
|
|
960
|
+
Process an object type schema into JSON Structure format.
|
|
961
|
+
|
|
962
|
+
Args:
|
|
963
|
+
json_type: The JSON Schema with type: "object"
|
|
964
|
+
record_name: Name of the containing record
|
|
965
|
+
field_name: Name of the field
|
|
966
|
+
namespace: Namespace
|
|
967
|
+
dependencies: Dependencies list
|
|
968
|
+
json_schema: Full JSON schema
|
|
969
|
+
base_uri: Base URI
|
|
970
|
+
structure_schema: Structure schema being built
|
|
971
|
+
record_stack: Record stack for recursion detection
|
|
972
|
+
recursion_depth: Current recursion depth
|
|
973
|
+
|
|
974
|
+
Returns:
|
|
975
|
+
dict: JSON Structure object definition
|
|
976
|
+
"""
|
|
977
|
+
properties = json_type.get('properties', {})
|
|
978
|
+
required = json_type.get('required', [])
|
|
979
|
+
|
|
980
|
+
structure_properties = {}
|
|
981
|
+
for prop_name, prop_schema in properties.items():
|
|
982
|
+
prop_type = self.json_type_to_structure_type(
|
|
983
|
+
prop_schema, record_name, prop_name, namespace, dependencies,
|
|
984
|
+
json_schema, base_uri, structure_schema, record_stack, recursion_depth + 1
|
|
985
|
+
)
|
|
986
|
+
prop_type = self._ensure_schema_object(prop_type, structure_schema, prop_name)
|
|
987
|
+
structure_properties[prop_name] = prop_type
|
|
988
|
+
|
|
989
|
+
result = {
|
|
990
|
+
'type': 'object',
|
|
991
|
+
'properties': structure_properties
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
if required:
|
|
995
|
+
result['required'] = required
|
|
996
|
+
|
|
997
|
+
return result
|
|
998
|
+
|
|
911
999
|
def add_alternate_names(self, structure: dict, original_name: str) -> dict:
|
|
912
1000
|
"""
|
|
913
1001
|
Add alternate names for different naming conventions.
|
|
@@ -1005,13 +1093,25 @@ class JsonToStructureConverter:
|
|
|
1005
1093
|
"""
|
|
1006
1094
|
Flatten the list of types in a union into a single list.
|
|
1007
1095
|
|
|
1096
|
+
JSON Structure Core requires union members to be either:
|
|
1097
|
+
- Simple type strings (e.g., "string", "null", "boolean")
|
|
1098
|
+
- References to definitions (e.g., {"$ref": "#/definitions/Foo"})
|
|
1099
|
+
|
|
1100
|
+
Inline compound types or primitives with constraints must be hoisted to definitions.
|
|
1101
|
+
|
|
1008
1102
|
Args:
|
|
1009
1103
|
type_list (list): The list of types in a union.
|
|
1104
|
+
structure_schema: The structure schema for hoisting definitions.
|
|
1105
|
+
name_hint: Hint for naming hoisted definitions.
|
|
1010
1106
|
|
|
1011
1107
|
Returns:
|
|
1012
1108
|
list: The flattened list of types.
|
|
1013
1109
|
"""
|
|
1014
1110
|
flat_list = []
|
|
1111
|
+
simple_primitives = {'string', 'boolean', 'integer', 'number', 'null', 'int8', 'int16',
|
|
1112
|
+
'int32', 'int64', 'float', 'double', 'bytes', 'uuid', 'datetime',
|
|
1113
|
+
'date', 'time', 'duration', 'decimal'}
|
|
1114
|
+
|
|
1015
1115
|
for idx, t in enumerate(type_list):
|
|
1016
1116
|
if isinstance(t, list):
|
|
1017
1117
|
inner = self.flatten_union(t, structure_schema, name_hint)
|
|
@@ -1019,17 +1119,52 @@ class JsonToStructureConverter:
|
|
|
1019
1119
|
obj = self._ensure_schema_object(u, structure_schema, f"{name_hint}_option_{idx}" if name_hint else None, force_hoist_in_union=True)
|
|
1020
1120
|
if obj not in flat_list:
|
|
1021
1121
|
flat_list.append(obj)
|
|
1022
|
-
|
|
1023
|
-
#
|
|
1024
|
-
if
|
|
1025
|
-
|
|
1122
|
+
elif isinstance(t, str):
|
|
1123
|
+
# Simple type string - use directly
|
|
1124
|
+
if t not in flat_list:
|
|
1125
|
+
flat_list.append(t)
|
|
1126
|
+
elif isinstance(t, dict):
|
|
1127
|
+
# Check if it's a simple primitive (type only, no constraints)
|
|
1128
|
+
if '$ref' in t:
|
|
1129
|
+
# Reference - use directly
|
|
1130
|
+
if t not in flat_list:
|
|
1131
|
+
flat_list.append(t)
|
|
1132
|
+
elif 'type' in t and t['type'] in simple_primitives and len(t) == 1:
|
|
1133
|
+
# Simple primitive type object like {"type": "boolean"} - extract the type string
|
|
1026
1134
|
if t['type'] not in flat_list:
|
|
1027
1135
|
flat_list.append(t['type'])
|
|
1136
|
+
elif 'type' in t and t['type'] in simple_primitives:
|
|
1137
|
+
# Primitive with constraints (e.g., {type: "string", maxLength: 280})
|
|
1138
|
+
# This needs to be hoisted because inline constraints not allowed in union
|
|
1139
|
+
if structure_schema is not None:
|
|
1140
|
+
hoisted = self._hoist_definition(t, structure_schema, f"{name_hint or 'union'}_{t['type']}")
|
|
1141
|
+
if hoisted not in flat_list:
|
|
1142
|
+
flat_list.append(hoisted)
|
|
1143
|
+
else:
|
|
1144
|
+
# No structure schema for hoisting - fallback to just the type
|
|
1145
|
+
if t['type'] not in flat_list:
|
|
1146
|
+
flat_list.append(t['type'])
|
|
1147
|
+
elif 'type' in t and t['type'] in ('array', 'object', 'set', 'map', 'choice'):
|
|
1148
|
+
# Compound type - must be hoisted
|
|
1149
|
+
if structure_schema is not None:
|
|
1150
|
+
hoisted = self._hoist_definition(t, structure_schema, f"{name_hint or 'union'}_{t['type']}")
|
|
1151
|
+
if hoisted not in flat_list:
|
|
1152
|
+
flat_list.append(hoisted)
|
|
1153
|
+
else:
|
|
1154
|
+
# No structure schema - use as is (will likely fail validation)
|
|
1155
|
+
obj = self._ensure_schema_object(t, structure_schema, f"{name_hint}_option_{idx}" if name_hint else None, force_hoist_in_union=True)
|
|
1156
|
+
if obj not in flat_list:
|
|
1157
|
+
flat_list.append(obj)
|
|
1028
1158
|
else:
|
|
1029
|
-
#
|
|
1159
|
+
# Other dict structure - use normal processing
|
|
1030
1160
|
obj = self._ensure_schema_object(t, structure_schema, f"{name_hint}_option_{idx}" if name_hint else None, force_hoist_in_union=True)
|
|
1031
1161
|
if obj not in flat_list:
|
|
1032
1162
|
flat_list.append(obj)
|
|
1163
|
+
else:
|
|
1164
|
+
# Unknown type - use normal processing
|
|
1165
|
+
obj = self._ensure_schema_object(t, structure_schema, f"{name_hint}_option_{idx}" if name_hint else None, force_hoist_in_union=True)
|
|
1166
|
+
if obj not in flat_list:
|
|
1167
|
+
flat_list.append(obj)
|
|
1033
1168
|
return flat_list
|
|
1034
1169
|
|
|
1035
1170
|
def merge_structure_schemas(self, schemas: list, structure_schemas: list, type_name: str | None = None, deps: List[str] = []) -> str | list | dict:
|
|
@@ -1244,6 +1379,61 @@ class JsonToStructureConverter:
|
|
|
1244
1379
|
'type': 'object',
|
|
1245
1380
|
'properties': {} }
|
|
1246
1381
|
|
|
1382
|
+
# Handle type arrays (e.g., ["integer", "null"] for nullable in JSON Schema 2020-12/OpenAPI 3.1)
|
|
1383
|
+
if json_type.get('type') and isinstance(json_type['type'], list):
|
|
1384
|
+
type_list = json_type['type']
|
|
1385
|
+
format_hint = json_type.get('format')
|
|
1386
|
+
enum_values = json_type.get('enum')
|
|
1387
|
+
|
|
1388
|
+
# Check if any type in the list is a compound type (array, object)
|
|
1389
|
+
compound_types = {'array', 'object'}
|
|
1390
|
+
has_compound = any(t in compound_types for t in type_list if isinstance(t, str))
|
|
1391
|
+
|
|
1392
|
+
if has_compound:
|
|
1393
|
+
# For compound types, we need to process each variant separately
|
|
1394
|
+
# and create a proper union. Compound types must be hoisted to definitions
|
|
1395
|
+
# because JSON Structure Core doesn't allow inline compound types in unions.
|
|
1396
|
+
union_members = []
|
|
1397
|
+
for t in type_list:
|
|
1398
|
+
if t == 'null':
|
|
1399
|
+
union_members.append('null')
|
|
1400
|
+
elif t == 'array':
|
|
1401
|
+
# Process array with the full schema context (items, etc.)
|
|
1402
|
+
array_schema = {**json_type, 'type': 'array'}
|
|
1403
|
+
array_type = self._process_array_type(
|
|
1404
|
+
array_schema, record_name, field_name, namespace, dependencies,
|
|
1405
|
+
json_schema, base_uri, structure_schema, record_stack, recursion_depth
|
|
1406
|
+
)
|
|
1407
|
+
# Hoist to definitions - inline compound types not allowed in union
|
|
1408
|
+
hoisted = self._hoist_definition(array_type, structure_schema, f"{field_name or record_name}_array")
|
|
1409
|
+
union_members.append(hoisted)
|
|
1410
|
+
elif t == 'object':
|
|
1411
|
+
# Process object with the full schema context (properties, etc.)
|
|
1412
|
+
object_schema = {**json_type, 'type': 'object'}
|
|
1413
|
+
object_type = self._process_object_type(
|
|
1414
|
+
object_schema, record_name, field_name, namespace, dependencies,
|
|
1415
|
+
json_schema, base_uri, structure_schema, record_stack, recursion_depth
|
|
1416
|
+
)
|
|
1417
|
+
# Hoist to definitions - inline compound types not allowed in union
|
|
1418
|
+
hoisted = self._hoist_definition(object_type, structure_schema, f"{field_name or record_name}_object")
|
|
1419
|
+
union_members.append(hoisted)
|
|
1420
|
+
elif isinstance(t, str):
|
|
1421
|
+
# Primitive type
|
|
1422
|
+
prim_type = self.json_schema_primitive_to_structure_type(
|
|
1423
|
+
t, format_hint, enum_values, record_name, field_name, namespace, dependencies, json_type
|
|
1424
|
+
)
|
|
1425
|
+
union_members.append(prim_type)
|
|
1426
|
+
|
|
1427
|
+
return {'type': self.flatten_union(union_members, structure_schema, field_name)}
|
|
1428
|
+
else:
|
|
1429
|
+
# All primitives, use the existing logic
|
|
1430
|
+
structure_type = self.json_schema_primitive_to_structure_type(
|
|
1431
|
+
type_list, format_hint, enum_values, record_name, field_name, namespace, dependencies, json_type
|
|
1432
|
+
)
|
|
1433
|
+
if isinstance(structure_type, dict):
|
|
1434
|
+
structure_type = self.add_validation_constraints(structure_type, json_type)
|
|
1435
|
+
return structure_type
|
|
1436
|
+
|
|
1247
1437
|
if json_type.get('type') and isinstance(json_type['type'], str):
|
|
1248
1438
|
# Check if this schema also has composition keywords that should be preserved
|
|
1249
1439
|
if self.preserve_composition and self.has_composition_keywords(json_type):
|
|
@@ -2160,12 +2350,20 @@ class JsonToStructureConverter:
|
|
|
2160
2350
|
if 'allOf' in json_type:
|
|
2161
2351
|
# Merge all schemas in allOf
|
|
2162
2352
|
merged = {}
|
|
2353
|
+
has_ref = False
|
|
2354
|
+
ref_value = None
|
|
2355
|
+
|
|
2163
2356
|
for schema in json_type['allOf']:
|
|
2164
2357
|
converted = self.json_type_to_structure_type(
|
|
2165
2358
|
schema, record_name, field_name, namespace, dependencies,
|
|
2166
2359
|
json_schema, base_uri, structure_schema, record_stack, recursion_depth + 1
|
|
2167
2360
|
)
|
|
2168
2361
|
if isinstance(converted, dict):
|
|
2362
|
+
# Track if we have a $ref
|
|
2363
|
+
if '$ref' in converted:
|
|
2364
|
+
has_ref = True
|
|
2365
|
+
ref_value = converted['$ref']
|
|
2366
|
+
|
|
2169
2367
|
# Simple merge - in real scenarios this would be more complex
|
|
2170
2368
|
for key, value in converted.items():
|
|
2171
2369
|
if key == 'properties' and key in merged:
|
|
@@ -2174,6 +2372,28 @@ class JsonToStructureConverter:
|
|
|
2174
2372
|
merged[key] = list(set(merged[key] + value))
|
|
2175
2373
|
else:
|
|
2176
2374
|
merged[key] = value
|
|
2375
|
+
|
|
2376
|
+
# JSON Structure doesn't allow both $ref and type
|
|
2377
|
+
# If we have a $ref and type with no meaningful properties, just use $ref
|
|
2378
|
+
if has_ref and 'type' in merged:
|
|
2379
|
+
has_meaningful_properties = (
|
|
2380
|
+
'properties' in merged and merged['properties'] or
|
|
2381
|
+
'required' in merged and merged['required'] or
|
|
2382
|
+
'items' in merged
|
|
2383
|
+
)
|
|
2384
|
+
if not has_meaningful_properties:
|
|
2385
|
+
# Just return the reference
|
|
2386
|
+
result = {'$ref': ref_value}
|
|
2387
|
+
# Copy metadata fields if present
|
|
2388
|
+
for meta_key in ['description', 'title', 'doc']:
|
|
2389
|
+
if meta_key in merged:
|
|
2390
|
+
result[meta_key] = merged[meta_key]
|
|
2391
|
+
return result
|
|
2392
|
+
else:
|
|
2393
|
+
# Has meaningful extensions - need to create a proper extension
|
|
2394
|
+
# Remove the $ref and keep the merged type
|
|
2395
|
+
del merged['$ref']
|
|
2396
|
+
|
|
2177
2397
|
return merged
|
|
2178
2398
|
|
|
2179
2399
|
elif 'anyOf' in json_type:
|
|
@@ -2186,8 +2406,10 @@ class JsonToStructureConverter:
|
|
|
2186
2406
|
)
|
|
2187
2407
|
anyof_schemas.append(converted)
|
|
2188
2408
|
|
|
2409
|
+
# Use flatten_union to properly hoist compound types
|
|
2410
|
+
flattened = self.flatten_union(anyof_schemas, structure_schema, field_name or record_name)
|
|
2189
2411
|
return {
|
|
2190
|
-
'type':
|
|
2412
|
+
'type': flattened
|
|
2191
2413
|
}
|
|
2192
2414
|
|
|
2193
2415
|
elif 'oneOf' in json_type:
|