structurize 3.7.4__tar.gz → 3.9.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.
- {structurize-3.7.4/structurize.egg-info → structurize-3.9.0}/PKG-INFO +1 -1
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/__init__.py +2 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/_version.py +3 -3
- structurize-3.9.0/avrotize/avrotoasn1.py +330 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotocsharp.py +11 -2
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotogo.py +63 -19
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotojava.py +243 -37
- structurize-3.9.0/avrotize/avrotojs.py +375 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotopython.py +58 -8
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotorust.py +209 -9
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotots.py +86 -11
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/commands.json +288 -14
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/common.py +84 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/constants.py +5 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/dependencies/typescript/node22/package.json +1 -1
- structurize-3.9.0/avrotize/rust_xml.py +21 -0
- structurize-3.9.0/avrotize/structuretoasn1.py +467 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/structuretocsharp.py +11 -2
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/structuretogo.py +48 -11
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/structuretojava.py +276 -15
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/structuretojs.py +123 -17
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/structuretopython.py +65 -7
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/structuretorust.py +214 -12
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/structuretots.py +90 -5
- {structurize-3.7.4 → structurize-3.9.0/structurize.egg-info}/PKG-INFO +1 -1
- {structurize-3.7.4 → structurize-3.9.0}/structurize.egg-info/SOURCES.txt +15 -0
- structurize-3.7.4/avrotize/avrotojs.py +0 -254
- {structurize-3.7.4 → structurize-3.9.0}/.gitignore +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/LICENSE +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/MANIFEST.in +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/README.md +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/__main__.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/asn1toavro.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotize.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotocapnproto.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotocpp.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotocsv.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotocue.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotodatapackage.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotodb.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotoflatbuffers.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotographql.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotoiceberg.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotojsons.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotojstruct.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotojtd.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotokusto.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotomd.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotools.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotoparquet.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotoproto.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotoraml.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotosmithy.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotosurreal.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotothrift.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrototsml.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrotoxsd.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/avrovalidator.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/capnprototoavro.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/cddltostructure.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/choice_inference.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/csvtoavro.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/cuetoavro.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/cuetostructure.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/datapackagetoavro.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/dependencies/cpp/vcpkg/vcpkg.json +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/dependency_resolver.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/dependency_version.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/flatbufferstoavro.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/flatbufferstojstruct.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/jsonstoavro.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/jsonstostructure.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/jsontoschema.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/jstructtoavro.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/jstructtoflatbuffers.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/jstructtoraml.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/jstructtosmithy.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/jtdtoavro.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/jtdtostructure.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/kstructtoavro.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/kustotoavro.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/kustotojstruct.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/mcp_server.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/openapitostructure.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/parquettoavro.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/proto2parser.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/proto3parser.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/prototoavro.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/ramltoavro.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/ramltojstruct.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/schema_inference.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/smithytoavro.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/smithytojstruct.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/sqltoavro.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/structuretocddl.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/structuretocpp.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/structuretocsv.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/structuretocue.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/structuretodatapackage.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/structuretodb.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/structuretographql.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/structuretoiceberg.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/structuretojsons.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/structuretojtd.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/structuretokusto.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/structuretomd.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/structuretoparquet.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/structuretoproto.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/structuretotsml.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/structuretoxsd.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/surrealtoavro.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/thriftstructure.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/thrifttoavro.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/tmslvalidate.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/validate.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/xmltoschema.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/avrotize/xsdtoavro.py +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/build.ps1 +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/build.sh +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/pyproject.toml +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/setup.cfg +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/structurize.egg-info/dependency_links.txt +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/structurize.egg-info/entry_points.txt +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/structurize.egg-info/requires.txt +0 -0
- {structurize-3.7.4 → structurize-3.9.0}/structurize.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: structurize
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.9.0
|
|
4
4
|
Summary: Tools to convert from and to JSON Structure from various other schema languages.
|
|
5
5
|
Author-email: Clemens Vasters <clemensv@microsoft.com>
|
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -51,6 +51,8 @@ _mappings = {
|
|
|
51
51
|
"convert_kafka_struct_to_avro_schema": (f"{mod}.kstructtoavro", "convert_kafka_struct_to_avro_schema"),
|
|
52
52
|
"convert_kusto_to_avro": (f"{mod}.kustotoavro", "convert_kusto_to_avro"),
|
|
53
53
|
"convert_asn1_to_avro": (f"{mod}.asn1toavro", "convert_asn1_to_avro"),
|
|
54
|
+
"convert_avro_to_asn1": (f"{mod}.avrotoasn1", "convert_avro_to_asn1"),
|
|
55
|
+
"convert_structure_to_asn1": (f"{mod}.structuretoasn1", "convert_structure_to_asn1"),
|
|
54
56
|
"convert_xsd_to_avro": (f"{mod}.xsdtoavro", "convert_xsd_to_avro"),
|
|
55
57
|
"convert_avro_to_json_schema": (f"{mod}.avrotojsons", "convert_avro_to_json_schema"),
|
|
56
58
|
"convert_avro_to_kusto_file": (f"{mod}.avrotokusto", "convert_avro_to_kusto_file"),
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '3.
|
|
22
|
-
__version_tuple__ = version_tuple = (3,
|
|
21
|
+
__version__ = version = '3.9.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (3, 9, 0)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g20391dedb'
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
# pylint: disable=line-too-long
|
|
2
|
+
|
|
3
|
+
""" AvroToASN1 class for converting Avrotize (Avro) schema to an ASN.1 module. """
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import re
|
|
8
|
+
from typing import Dict, List, Union
|
|
9
|
+
|
|
10
|
+
from avrotize.common import is_generic_avro_type
|
|
11
|
+
|
|
12
|
+
JsonNode = Union[Dict, List, str, int, float, bool, None]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AvroToASN1:
|
|
16
|
+
""" Converts an Avrotize (Avro) schema to an ASN.1 (X.680) module. """
|
|
17
|
+
|
|
18
|
+
def __init__(self, module_name: str = 'AvrotizeSchema'):
|
|
19
|
+
self.module_name = self._to_type_reference(module_name)
|
|
20
|
+
# Ordered list of top-level "Name ::= ..." assignment strings.
|
|
21
|
+
self.assignments: List[str] = []
|
|
22
|
+
# ASN.1 type names already emitted (global uniqueness for typereferences).
|
|
23
|
+
self.known_types: Dict[str, str] = {}
|
|
24
|
+
# Avro full name -> ASN.1 type name.
|
|
25
|
+
self.type_name_map: Dict[str, str] = {}
|
|
26
|
+
self.used_type_names: set = set()
|
|
27
|
+
|
|
28
|
+
# ------------------------------------------------------------------ names
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def _sanitize(name: str) -> str:
|
|
32
|
+
"""Turn an arbitrary name into an ASN.1-legal token body (letters, digits, hyphens)."""
|
|
33
|
+
if not name:
|
|
34
|
+
return 'x'
|
|
35
|
+
# Keep only the last dotted segment for namespaced names.
|
|
36
|
+
name = name.split('.')[-1]
|
|
37
|
+
# Replace any run of illegal characters with a single hyphen.
|
|
38
|
+
name = re.sub(r'[^A-Za-z0-9]+', '-', name)
|
|
39
|
+
# Collapse consecutive hyphens and strip leading/trailing hyphens.
|
|
40
|
+
name = re.sub(r'-+', '-', name).strip('-')
|
|
41
|
+
if not name:
|
|
42
|
+
return 'x'
|
|
43
|
+
# ASN.1 tokens must start with a letter.
|
|
44
|
+
if not name[0].isalpha():
|
|
45
|
+
name = 'x-' + name
|
|
46
|
+
return name
|
|
47
|
+
|
|
48
|
+
def _to_type_reference(self, name: str) -> str:
|
|
49
|
+
"""ASN.1 typereference: must start with an upper-case letter."""
|
|
50
|
+
token = self._sanitize(name)
|
|
51
|
+
return token[0].upper() + token[1:]
|
|
52
|
+
|
|
53
|
+
def _to_identifier(self, name: str) -> str:
|
|
54
|
+
"""ASN.1 identifier (member / enum value): must start with a lower-case letter."""
|
|
55
|
+
token = self._sanitize(name)
|
|
56
|
+
letters = [c for c in token if c.isalpha()]
|
|
57
|
+
if letters and all(c.isupper() for c in letters):
|
|
58
|
+
# ALL-CAPS tokens (e.g. Avro enum symbols) read best fully lower-cased.
|
|
59
|
+
token = token.lower()
|
|
60
|
+
return token[0].lower() + token[1:]
|
|
61
|
+
|
|
62
|
+
def _unique_type_name(self, name: str) -> str:
|
|
63
|
+
"""Return a module-unique ASN.1 typereference for the given name."""
|
|
64
|
+
base = self._to_type_reference(name)
|
|
65
|
+
candidate = base
|
|
66
|
+
counter = 1
|
|
67
|
+
while candidate in self.used_type_names:
|
|
68
|
+
counter += 1
|
|
69
|
+
candidate = f"{base}{counter}"
|
|
70
|
+
self.used_type_names.add(candidate)
|
|
71
|
+
return candidate
|
|
72
|
+
|
|
73
|
+
# ------------------------------------------------------------- primitives
|
|
74
|
+
|
|
75
|
+
def _map_primitive(self, avro_type: Union[str, dict]) -> str:
|
|
76
|
+
"""Map an Avro primitive (or logical) type to an ASN.1 built-in type."""
|
|
77
|
+
if isinstance(avro_type, dict):
|
|
78
|
+
logical_type = avro_type.get('logicalType')
|
|
79
|
+
base = avro_type.get('type')
|
|
80
|
+
if logical_type == 'date':
|
|
81
|
+
return 'DATE'
|
|
82
|
+
if logical_type in ('timestamp-millis', 'timestamp-micros',
|
|
83
|
+
'local-timestamp-millis', 'local-timestamp-micros'):
|
|
84
|
+
return 'DATE-TIME'
|
|
85
|
+
if logical_type in ('time-millis', 'time-micros'):
|
|
86
|
+
return 'TIME-OF-DAY'
|
|
87
|
+
if logical_type == 'decimal':
|
|
88
|
+
return 'REAL'
|
|
89
|
+
if logical_type == 'uuid':
|
|
90
|
+
return 'UTF8String'
|
|
91
|
+
if logical_type == 'duration':
|
|
92
|
+
return 'UTF8String'
|
|
93
|
+
return self._map_primitive(base)
|
|
94
|
+
|
|
95
|
+
mapping = {
|
|
96
|
+
'null': 'NULL',
|
|
97
|
+
'boolean': 'BOOLEAN',
|
|
98
|
+
'int': 'INTEGER',
|
|
99
|
+
'long': 'INTEGER',
|
|
100
|
+
'float': 'REAL',
|
|
101
|
+
'double': 'REAL',
|
|
102
|
+
'bytes': 'OCTET STRING',
|
|
103
|
+
'string': 'UTF8String',
|
|
104
|
+
}
|
|
105
|
+
return mapping.get(avro_type, '')
|
|
106
|
+
|
|
107
|
+
@staticmethod
|
|
108
|
+
def _is_primitive(avro_type: Union[str, dict]) -> bool:
|
|
109
|
+
if isinstance(avro_type, dict):
|
|
110
|
+
return 'logicalType' in avro_type or avro_type.get('type') in {
|
|
111
|
+
'null', 'boolean', 'int', 'long', 'float', 'double', 'bytes', 'string'}
|
|
112
|
+
return avro_type in {'null', 'boolean', 'int', 'long', 'float', 'double', 'bytes', 'string'}
|
|
113
|
+
|
|
114
|
+
# ------------------------------------------------------------- expressions
|
|
115
|
+
|
|
116
|
+
def type_expression(self, avro_type: JsonNode) -> str:
|
|
117
|
+
"""Return an ASN.1 type expression for an Avro type (defining named types as a side effect)."""
|
|
118
|
+
if isinstance(avro_type, str):
|
|
119
|
+
if self._is_primitive(avro_type):
|
|
120
|
+
return self._map_primitive(avro_type)
|
|
121
|
+
# Reference to a previously defined named type.
|
|
122
|
+
return self.type_name_map.get(avro_type, self._to_type_reference(avro_type))
|
|
123
|
+
|
|
124
|
+
if isinstance(avro_type, list):
|
|
125
|
+
return self._union_expression(avro_type)
|
|
126
|
+
|
|
127
|
+
if isinstance(avro_type, dict):
|
|
128
|
+
if 'logicalType' in avro_type:
|
|
129
|
+
return self._map_primitive(avro_type)
|
|
130
|
+
kind = avro_type.get('type')
|
|
131
|
+
if kind == 'record' or kind == 'error':
|
|
132
|
+
return self._define_record(avro_type)
|
|
133
|
+
if kind == 'enum':
|
|
134
|
+
return self._define_enum(avro_type)
|
|
135
|
+
if kind == 'fixed':
|
|
136
|
+
return self._define_fixed(avro_type)
|
|
137
|
+
if kind == 'array':
|
|
138
|
+
items = avro_type.get('items', 'string')
|
|
139
|
+
return f"SEQUENCE OF {self.type_expression(items)}"
|
|
140
|
+
if kind == 'map':
|
|
141
|
+
values = avro_type.get('values', 'string')
|
|
142
|
+
return f"SEQUENCE OF SEQUENCE {{ key UTF8String, value {self.type_expression(values)} }}"
|
|
143
|
+
if self._is_primitive(avro_type):
|
|
144
|
+
return self._map_primitive(avro_type)
|
|
145
|
+
if isinstance(kind, (dict, list)):
|
|
146
|
+
return self.type_expression(kind)
|
|
147
|
+
if isinstance(kind, str):
|
|
148
|
+
return self.type_name_map.get(kind, self._to_type_reference(kind))
|
|
149
|
+
return 'UTF8String'
|
|
150
|
+
|
|
151
|
+
def _union_expression(self, union: List[JsonNode]) -> str:
|
|
152
|
+
"""Map an Avro union to an ASN.1 type expression (nullability is handled by the caller)."""
|
|
153
|
+
if is_generic_avro_type(union):
|
|
154
|
+
return 'ANY'
|
|
155
|
+
non_null = [t for t in union if t != 'null']
|
|
156
|
+
if len(non_null) == 0:
|
|
157
|
+
return 'NULL'
|
|
158
|
+
if len(non_null) == 1:
|
|
159
|
+
return self.type_expression(non_null[0])
|
|
160
|
+
members = []
|
|
161
|
+
used: set = set()
|
|
162
|
+
for i, branch in enumerate(non_null):
|
|
163
|
+
alt = self._alternative_name(branch, i, used)
|
|
164
|
+
members.append(f"{alt} {self.type_expression(branch)}")
|
|
165
|
+
return "CHOICE { " + ", ".join(members) + " }"
|
|
166
|
+
|
|
167
|
+
def _alternative_name(self, branch: JsonNode, index: int, used: set) -> str:
|
|
168
|
+
"""Derive a unique ASN.1 CHOICE alternative identifier from a union branch."""
|
|
169
|
+
if isinstance(branch, str):
|
|
170
|
+
base = self._to_identifier(branch)
|
|
171
|
+
elif isinstance(branch, dict):
|
|
172
|
+
if 'name' in branch:
|
|
173
|
+
base = self._to_identifier(branch['name'])
|
|
174
|
+
else:
|
|
175
|
+
base = self._to_identifier(str(branch.get('type', f'choice{index}')))
|
|
176
|
+
else:
|
|
177
|
+
base = f'choice{index}'
|
|
178
|
+
candidate = base
|
|
179
|
+
counter = 1
|
|
180
|
+
while candidate in used:
|
|
181
|
+
counter += 1
|
|
182
|
+
candidate = f"{base}{counter}"
|
|
183
|
+
used.add(candidate)
|
|
184
|
+
return candidate
|
|
185
|
+
|
|
186
|
+
# --------------------------------------------------------------- defining
|
|
187
|
+
|
|
188
|
+
def _define_record(self, record: dict) -> str:
|
|
189
|
+
"""Emit an ASN.1 SEQUENCE assignment for an Avro record, returning its type name."""
|
|
190
|
+
full_name = self._full_name(record)
|
|
191
|
+
if full_name in self.type_name_map:
|
|
192
|
+
return self.type_name_map[full_name]
|
|
193
|
+
|
|
194
|
+
type_name = self._unique_type_name(record.get('name', 'Record'))
|
|
195
|
+
# Register before recursing so self-references resolve.
|
|
196
|
+
self.type_name_map[full_name] = type_name
|
|
197
|
+
|
|
198
|
+
fields = record.get('fields', [])
|
|
199
|
+
used_members: set = set()
|
|
200
|
+
members = [(self._member_definition(field, used_members), field.get('doc'))
|
|
201
|
+
for field in fields]
|
|
202
|
+
|
|
203
|
+
if members:
|
|
204
|
+
lines: List[str] = []
|
|
205
|
+
for i, (core, doc) in enumerate(members):
|
|
206
|
+
comma = "," if i < len(members) - 1 else ""
|
|
207
|
+
if doc:
|
|
208
|
+
lines.append(f" -- {self._comment(doc)}")
|
|
209
|
+
lines.append(f" {core}{comma}")
|
|
210
|
+
body = "SEQUENCE {\n" + "\n".join(lines) + "\n}"
|
|
211
|
+
else:
|
|
212
|
+
body = "SEQUENCE {}"
|
|
213
|
+
self._append_assignment(type_name, body, record.get('doc'))
|
|
214
|
+
return type_name
|
|
215
|
+
|
|
216
|
+
def _member_definition(self, field: dict, used_members: set) -> str:
|
|
217
|
+
"""Build a single ASN.1 SEQUENCE member (without comment) from an Avro field."""
|
|
218
|
+
name = self._unique_identifier(field['name'], used_members)
|
|
219
|
+
field_type = field['type']
|
|
220
|
+
optional = isinstance(field_type, list) and 'null' in field_type
|
|
221
|
+
expr = self.type_expression(field_type)
|
|
222
|
+
member = f"{name} {expr}"
|
|
223
|
+
if optional:
|
|
224
|
+
member += " OPTIONAL"
|
|
225
|
+
return member
|
|
226
|
+
|
|
227
|
+
def _define_enum(self, enum: dict) -> str:
|
|
228
|
+
"""Emit an ASN.1 ENUMERATED assignment for an Avro enum, returning its type name."""
|
|
229
|
+
full_name = self._full_name(enum)
|
|
230
|
+
if full_name in self.type_name_map:
|
|
231
|
+
return self.type_name_map[full_name]
|
|
232
|
+
|
|
233
|
+
type_name = self._unique_type_name(enum.get('name', 'Enum'))
|
|
234
|
+
self.type_name_map[full_name] = type_name
|
|
235
|
+
|
|
236
|
+
values: List[str] = []
|
|
237
|
+
used_symbols: set = set()
|
|
238
|
+
for i, symbol in enumerate(enum.get('symbols', [])):
|
|
239
|
+
sym = self._unique_identifier(symbol, used_symbols)
|
|
240
|
+
values.append(f"{sym}({i})")
|
|
241
|
+
body = "ENUMERATED { " + ", ".join(values) + " }" if values else "ENUMERATED { unspecified(0) }"
|
|
242
|
+
self._append_assignment(type_name, body, enum.get('doc'))
|
|
243
|
+
return type_name
|
|
244
|
+
|
|
245
|
+
def _define_fixed(self, fixed: dict) -> str:
|
|
246
|
+
"""Emit an ASN.1 OCTET STRING (SIZE(n)) assignment for an Avro fixed type."""
|
|
247
|
+
full_name = self._full_name(fixed)
|
|
248
|
+
if full_name in self.type_name_map:
|
|
249
|
+
return self.type_name_map[full_name]
|
|
250
|
+
|
|
251
|
+
type_name = self._unique_type_name(fixed.get('name', 'Fixed'))
|
|
252
|
+
self.type_name_map[full_name] = type_name
|
|
253
|
+
size = fixed.get('size', 0)
|
|
254
|
+
self._append_assignment(type_name, f"OCTET STRING (SIZE({size}))", fixed.get('doc'))
|
|
255
|
+
return type_name
|
|
256
|
+
|
|
257
|
+
# ------------------------------------------------------------- utilities
|
|
258
|
+
|
|
259
|
+
def _unique_identifier(self, name: str, used: set) -> str:
|
|
260
|
+
base = self._to_identifier(name)
|
|
261
|
+
candidate = base
|
|
262
|
+
counter = 1
|
|
263
|
+
while candidate in used:
|
|
264
|
+
counter += 1
|
|
265
|
+
candidate = f"{base}-{counter}"
|
|
266
|
+
used.add(candidate)
|
|
267
|
+
return candidate
|
|
268
|
+
|
|
269
|
+
@staticmethod
|
|
270
|
+
def _full_name(schema: dict) -> str:
|
|
271
|
+
name = schema.get('name', '')
|
|
272
|
+
namespace = schema.get('namespace', '')
|
|
273
|
+
return f"{namespace}.{name}" if namespace else name
|
|
274
|
+
|
|
275
|
+
@staticmethod
|
|
276
|
+
def _comment(text: str) -> str:
|
|
277
|
+
"""ASN.1 line comment body: no line breaks, no double-hyphen terminator."""
|
|
278
|
+
return re.sub(r'\s+', ' ', str(text)).replace('--', '- -').strip()
|
|
279
|
+
|
|
280
|
+
def _append_assignment(self, type_name: str, body: str, doc: str = None) -> None:
|
|
281
|
+
assignment = ''
|
|
282
|
+
if doc:
|
|
283
|
+
assignment += f"-- {self._comment(doc)}\n"
|
|
284
|
+
assignment += f"{type_name} ::= {body}"
|
|
285
|
+
self.assignments.append(assignment)
|
|
286
|
+
self.known_types[type_name] = body
|
|
287
|
+
|
|
288
|
+
# ----------------------------------------------------------------- render
|
|
289
|
+
|
|
290
|
+
def convert(self, avro_schema: JsonNode) -> str:
|
|
291
|
+
"""Convert an Avro schema (dict or list of named types) to an ASN.1 module string."""
|
|
292
|
+
schemas = avro_schema if isinstance(avro_schema, list) else [avro_schema]
|
|
293
|
+
for schema in schemas:
|
|
294
|
+
if not isinstance(schema, dict):
|
|
295
|
+
continue
|
|
296
|
+
kind = schema.get('type')
|
|
297
|
+
if kind in ('record', 'error'):
|
|
298
|
+
self._define_record(schema)
|
|
299
|
+
elif kind == 'enum':
|
|
300
|
+
self._define_enum(schema)
|
|
301
|
+
elif kind == 'fixed':
|
|
302
|
+
self._define_fixed(schema)
|
|
303
|
+
return self.render()
|
|
304
|
+
|
|
305
|
+
def render(self) -> str:
|
|
306
|
+
"""Render the accumulated assignments as a complete ASN.1 module."""
|
|
307
|
+
lines = [f"{self.module_name} DEFINITIONS AUTOMATIC TAGS ::= BEGIN", ""]
|
|
308
|
+
for assignment in self.assignments:
|
|
309
|
+
lines.append(assignment)
|
|
310
|
+
lines.append("")
|
|
311
|
+
lines.append("END")
|
|
312
|
+
return "\n".join(lines) + "\n"
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def convert_avro_to_asn1(avro_schema_path: str, asn1_file_path: str, module_name: str = '') -> None:
|
|
316
|
+
"""Convert an Avrotize schema file to an ASN.1 module file."""
|
|
317
|
+
with open(avro_schema_path, 'r', encoding='utf-8') as avro_file:
|
|
318
|
+
avro_schema = json.load(avro_file)
|
|
319
|
+
|
|
320
|
+
if not module_name:
|
|
321
|
+
module_name = os.path.splitext(os.path.basename(asn1_file_path))[0] or 'AvrotizeSchema'
|
|
322
|
+
|
|
323
|
+
converter = AvroToASN1(module_name)
|
|
324
|
+
asn1_text = converter.convert(avro_schema)
|
|
325
|
+
|
|
326
|
+
out_dir = os.path.dirname(asn1_file_path)
|
|
327
|
+
if out_dir and not os.path.exists(out_dir):
|
|
328
|
+
os.makedirs(out_dir, exist_ok=True)
|
|
329
|
+
with open(asn1_file_path, 'w', encoding='utf-8') as asn1_file:
|
|
330
|
+
asn1_file.write(asn1_text)
|
|
@@ -58,6 +58,7 @@ class AvroToCSharp:
|
|
|
58
58
|
self.avro_annotation = False
|
|
59
59
|
self.protobuf_net_annotation = False
|
|
60
60
|
self.openapi_generator_compat = False
|
|
61
|
+
self.target_framework = ''
|
|
61
62
|
self.generated_types: Dict[str,str] = {}
|
|
62
63
|
self.generated_avro_types: Dict[str, Dict[str, Union[str, Dict, List]]] = {}
|
|
63
64
|
self.type_dict: Dict[str, Dict] = {}
|
|
@@ -1189,6 +1190,7 @@ class AvroToCSharp:
|
|
|
1189
1190
|
file.write(process_template(
|
|
1190
1191
|
"avrotocsharp/project.csproj.jinja",
|
|
1191
1192
|
project_name=project_name,
|
|
1193
|
+
target_framework=self.target_framework,
|
|
1192
1194
|
avro_annotation=self.avro_annotation,
|
|
1193
1195
|
system_xml_annotation=self.system_xml_annotation,
|
|
1194
1196
|
system_text_json_annotation=self.system_text_json_annotation,
|
|
@@ -1216,6 +1218,7 @@ class AvroToCSharp:
|
|
|
1216
1218
|
file.write(process_template(
|
|
1217
1219
|
"avrotocsharp/testproject.csproj.jinja",
|
|
1218
1220
|
project_name=project_name,
|
|
1221
|
+
target_framework=self.target_framework,
|
|
1219
1222
|
avro_annotation=self.avro_annotation,
|
|
1220
1223
|
system_xml_annotation=self.system_xml_annotation,
|
|
1221
1224
|
system_text_json_annotation=self.system_text_json_annotation,
|
|
@@ -1279,7 +1282,8 @@ def convert_avro_to_csharp(
|
|
|
1279
1282
|
cbor_annotation=False,
|
|
1280
1283
|
avro_annotation=False,
|
|
1281
1284
|
protobuf_net_annotation=False,
|
|
1282
|
-
openapi_generator_compat=False
|
|
1285
|
+
openapi_generator_compat=False,
|
|
1286
|
+
target_framework=''
|
|
1283
1287
|
):
|
|
1284
1288
|
"""Converts Avro schema to C# classes
|
|
1285
1289
|
|
|
@@ -1297,6 +1301,7 @@ def convert_avro_to_csharp(
|
|
|
1297
1301
|
avro_annotation (bool, optional): Use Avro annotations. Defaults to False.
|
|
1298
1302
|
protobuf_net_annotation (bool, optional): Use protobuf-net annotations. Defaults to False.
|
|
1299
1303
|
openapi_generator_compat (bool, optional): Generate OpenAPI Generator-compatible C# models. Defaults to False.
|
|
1304
|
+
target_framework (str, optional): Target framework moniker written into the generated .csproj (e.g. 'net8.0'). A ';'-separated value emits <TargetFrameworks> for multi-targeting. Defaults to '' (net10.0).
|
|
1300
1305
|
"""
|
|
1301
1306
|
|
|
1302
1307
|
if not base_namespace:
|
|
@@ -1312,6 +1317,7 @@ def convert_avro_to_csharp(
|
|
|
1312
1317
|
avrotocs.avro_annotation = avro_annotation
|
|
1313
1318
|
avrotocs.protobuf_net_annotation = protobuf_net_annotation
|
|
1314
1319
|
avrotocs.openapi_generator_compat = openapi_generator_compat
|
|
1320
|
+
avrotocs.target_framework = target_framework
|
|
1315
1321
|
avrotocs.convert(avro_schema_path, cs_file_path)
|
|
1316
1322
|
|
|
1317
1323
|
|
|
@@ -1328,7 +1334,8 @@ def convert_avro_schema_to_csharp(
|
|
|
1328
1334
|
cbor_annotation: bool = False,
|
|
1329
1335
|
avro_annotation: bool = False,
|
|
1330
1336
|
protobuf_net_annotation: bool = False,
|
|
1331
|
-
openapi_generator_compat: bool = False
|
|
1337
|
+
openapi_generator_compat: bool = False,
|
|
1338
|
+
target_framework: str = ''
|
|
1332
1339
|
):
|
|
1333
1340
|
"""Converts Avro schema to C# classes
|
|
1334
1341
|
|
|
@@ -1345,6 +1352,7 @@ def convert_avro_schema_to_csharp(
|
|
|
1345
1352
|
cbor_annotation (bool, optional): Use Dahomey.Cbor annotations. Defaults to False.
|
|
1346
1353
|
avro_annotation (bool, optional): Use Avro annotations. Defaults to False.
|
|
1347
1354
|
protobuf_net_annotation (bool, optional): Use protobuf-net annotations. Defaults to False.
|
|
1355
|
+
target_framework (str, optional): Target framework moniker written into the generated .csproj (e.g. 'net8.0'). A ';'-separated value emits <TargetFrameworks> for multi-targeting. Defaults to '' (net10.0).
|
|
1348
1356
|
"""
|
|
1349
1357
|
avrotocs = AvroToCSharp(base_namespace)
|
|
1350
1358
|
avrotocs.project_name = project_name
|
|
@@ -1357,4 +1365,5 @@ def convert_avro_schema_to_csharp(
|
|
|
1357
1365
|
avrotocs.avro_annotation = avro_annotation
|
|
1358
1366
|
avrotocs.protobuf_net_annotation = protobuf_net_annotation
|
|
1359
1367
|
avrotocs.openapi_generator_compat = openapi_generator_compat
|
|
1368
|
+
avrotocs.target_framework = target_framework
|
|
1360
1369
|
avrotocs.convert_schema(avro_schema, output_dir)
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
3
|
from typing import Dict, List, Union, Set
|
|
4
|
-
from avrotize.common import get_longest_namespace_prefix, is_generic_avro_type, is_any_value_type, pascal,
|
|
4
|
+
from avrotize.common import (get_longest_namespace_prefix, is_generic_avro_type, is_any_value_type, pascal,
|
|
5
|
+
render_template, format_go_files, xml_wire_name, xml_enum_wire_value)
|
|
5
6
|
|
|
6
7
|
INDENT = ' '
|
|
7
8
|
|
|
@@ -26,6 +27,7 @@ class AvroToGo:
|
|
|
26
27
|
self.referenced_packages_stack: List[Dict[str, Set[str]]] = []
|
|
27
28
|
self.avro_annotation = False
|
|
28
29
|
self.json_annotation = False
|
|
30
|
+
self.xml_annotation = False
|
|
29
31
|
self.longest_common_prefix = ''
|
|
30
32
|
self.package_site = 'github.com'
|
|
31
33
|
self.package_username = 'username'
|
|
@@ -33,10 +35,19 @@ class AvroToGo:
|
|
|
33
35
|
self.enums = []
|
|
34
36
|
|
|
35
37
|
def _safe_package_name(self, name: str) -> str:
|
|
36
|
-
"""Converts a name to a safe Go package name
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
"""Converts a name to a safe Go package name (a valid lowercase identifier).
|
|
39
|
+
|
|
40
|
+
Sanitizes any character that is not valid in a Go identifier (dots,
|
|
41
|
+
hyphens from file names, etc.) to underscores so the emitted ``package``
|
|
42
|
+
clause is compilable and gofmt-parseable.
|
|
43
|
+
"""
|
|
44
|
+
import re
|
|
45
|
+
safe = re.sub(r'[^a-zA-Z0-9_]+', '_', name).strip('_').lower()
|
|
46
|
+
if not safe or not re.match(r'^[a-z]', safe):
|
|
47
|
+
safe = f"pkg_{safe}" if safe else "pkg"
|
|
48
|
+
if safe in self.GO_RESERVED_WORDS:
|
|
49
|
+
return f"{safe}_"
|
|
50
|
+
return safe
|
|
40
51
|
|
|
41
52
|
def safe_identifier(self, name: str) -> str:
|
|
42
53
|
"""Converts a name to a safe Go identifier"""
|
|
@@ -117,9 +128,8 @@ class AvroToGo:
|
|
|
117
128
|
return f"[]{item_type}"
|
|
118
129
|
elif avro_type['type'] == 'map':
|
|
119
130
|
values_type = self.convert_avro_type_to_go(field_name, avro_type['values'], nullable=True, parent_namespace=parent_namespace)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
return f"map[string]{values_type}"
|
|
131
|
+
map_type = f"map[string]{values_type}"
|
|
132
|
+
return f"XMLMap[{values_type}]" if self.xml_annotation else map_type
|
|
123
133
|
elif 'logicalType' in avro_type:
|
|
124
134
|
if avro_type['logicalType'] == 'date':
|
|
125
135
|
return 'time.Time'
|
|
@@ -162,11 +172,20 @@ class AvroToGo:
|
|
|
162
172
|
self.generated_types_avro_namespace[avro_fullname] = "struct"
|
|
163
173
|
self.generated_types_go_package[go_struct_name] = "struct"
|
|
164
174
|
|
|
165
|
-
fields = [
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
175
|
+
fields = []
|
|
176
|
+
for field in avro_schema.get('fields', []):
|
|
177
|
+
field_type = self.convert_avro_type_to_go(field['name'], field['type'], parent_namespace=namespace)
|
|
178
|
+
raw_type = field['type']
|
|
179
|
+
fields.append({
|
|
180
|
+
'name': pascal(field['name']),
|
|
181
|
+
'type': field_type,
|
|
182
|
+
'original_name': field['name'],
|
|
183
|
+
'xml_name': xml_wire_name(field['name'], field),
|
|
184
|
+
'xml_kind': field.get('xmlkind', 'element'),
|
|
185
|
+
'xml_required': not (isinstance(raw_type, list) and 'null' in raw_type) and not (isinstance(raw_type, dict) and raw_type.get('type') in ('array', 'map')),
|
|
186
|
+
'xml_repeated': isinstance(raw_type, dict) and raw_type.get('type') == 'array',
|
|
187
|
+
'xml_ambiguous': field_type.lstrip('*') == 'interface{}',
|
|
188
|
+
})
|
|
170
189
|
|
|
171
190
|
# Collect imports from field types
|
|
172
191
|
go_types = [f['type'] for f in fields]
|
|
@@ -178,6 +197,10 @@ class AvroToGo:
|
|
|
178
197
|
'fields': fields,
|
|
179
198
|
'avro_schema': json.dumps(avro_schema),
|
|
180
199
|
'json_annotation': self.json_annotation,
|
|
200
|
+
'xml_annotation': self.xml_annotation,
|
|
201
|
+
'xml_name': xml_wire_name(avro_schema['name'], avro_schema),
|
|
202
|
+
'xml_namespace': avro_schema.get('xmlns', ''),
|
|
203
|
+
'needs_xml_name': bool(avro_schema.get('xmlns')) or xml_wire_name(avro_schema['name'], avro_schema) != go_struct_name,
|
|
181
204
|
'avro_annotation': self.avro_annotation,
|
|
182
205
|
'json_match_predicates': [self.get_is_json_match_clause(f['name'], f['type']) for f in fields],
|
|
183
206
|
'base_package': self.base_package,
|
|
@@ -214,9 +237,14 @@ class AvroToGo:
|
|
|
214
237
|
context = {
|
|
215
238
|
'doc': avro_schema.get('doc', ''),
|
|
216
239
|
'struct_name': enum_name,
|
|
217
|
-
'symbols':
|
|
240
|
+
'symbols': [
|
|
241
|
+
{'name': symbol, 'xml_value': xml_enum_wire_value(symbol, avro_schema)}
|
|
242
|
+
for symbol in avro_schema.get('symbols', [])
|
|
243
|
+
],
|
|
218
244
|
'imports': imports,
|
|
219
245
|
'base_package': self.base_package,
|
|
246
|
+
'xml_annotation': self.xml_annotation,
|
|
247
|
+
'string_enum': self.avro_annotation,
|
|
220
248
|
'referenced_packages': self.referenced_packages.keys()
|
|
221
249
|
}
|
|
222
250
|
|
|
@@ -229,9 +257,10 @@ class AvroToGo:
|
|
|
229
257
|
self.enums.append({
|
|
230
258
|
'name': enum_name,
|
|
231
259
|
'symbols': avro_schema.get('symbols', []),
|
|
260
|
+
'string_enum': self.avro_annotation,
|
|
232
261
|
})
|
|
233
262
|
|
|
234
|
-
self.generate_unit_test('enum', enum_name,
|
|
263
|
+
self.generate_unit_test('enum', enum_name, avro_schema.get('symbols', []))
|
|
235
264
|
|
|
236
265
|
return enum_name
|
|
237
266
|
|
|
@@ -247,6 +276,7 @@ class AvroToGo:
|
|
|
247
276
|
'union_class_name': union_class_name,
|
|
248
277
|
'union_types': union_types,
|
|
249
278
|
'json_annotation': self.json_annotation,
|
|
279
|
+
'xml_annotation': self.xml_annotation,
|
|
250
280
|
'avro_annotation': self.avro_annotation,
|
|
251
281
|
'get_is_json_match_clause': self.get_is_json_match_clause,
|
|
252
282
|
'base_package': self.base_package,
|
|
@@ -294,14 +324,14 @@ class AvroToGo:
|
|
|
294
324
|
return f"if _, ok := node[\"{field_name}\"].([]byte); !ok {{ return false }}"
|
|
295
325
|
elif field_type == 'interface{}':
|
|
296
326
|
return f"if _, ok := node[\"{field_name}\"].(interface{{}}); !ok {{ return false }}"
|
|
297
|
-
elif field_type.startswith('map[string]'):
|
|
327
|
+
elif field_type.startswith('map[string]') or field_type.startswith('XMLMap['):
|
|
298
328
|
return f"if _, ok := node[\"{field_name}\"].(map[string]interface{{}}); !ok {{ return false }}"
|
|
299
329
|
elif field_type.startswith('[]'):
|
|
300
330
|
return f"if _, ok := node[\"{field_name}\"].([]interface{{}}); !ok {{ return false }}"
|
|
301
331
|
elif field_type in self.generated_types_go_package:
|
|
302
332
|
return f"if _, ok := node[\"{field_name}\"].({field_type}); !ok {{ return false }}"
|
|
303
333
|
else:
|
|
304
|
-
return f"if _, ok := node[\"{field_name}\"].(map[string
|
|
334
|
+
return f"if _, ok := node[\"{field_name}\"].(map[string]interface{{}}); !ok {{ return false }}"
|
|
305
335
|
|
|
306
336
|
def get_imports_for_definition(self, types: List[str]) -> Set[str]:
|
|
307
337
|
"""Collects necessary imports for the Go definition based on the Go types"""
|
|
@@ -393,6 +423,7 @@ class AvroToGo:
|
|
|
393
423
|
'package_site': self.package_site,
|
|
394
424
|
'package_username': self.package_username,
|
|
395
425
|
'json_annotation': self.json_annotation,
|
|
426
|
+
'xml_annotation': self.xml_annotation,
|
|
396
427
|
'avro_annotation': self.avro_annotation
|
|
397
428
|
}
|
|
398
429
|
|
|
@@ -418,6 +449,15 @@ class AvroToGo:
|
|
|
418
449
|
self.write_go_mod_file()
|
|
419
450
|
self.write_modname_go_file()
|
|
420
451
|
self.generate_helpers()
|
|
452
|
+
self.write_xml_helpers()
|
|
453
|
+
format_go_files(self.output_dir)
|
|
454
|
+
|
|
455
|
+
def write_xml_helpers(self) -> None:
|
|
456
|
+
"""Write generic XML support for map fields."""
|
|
457
|
+
if not self.xml_annotation:
|
|
458
|
+
return
|
|
459
|
+
helper_path = os.path.join(self.output_dir, 'pkg', self.base_package, 'xml_helpers.go')
|
|
460
|
+
render_template('avrotogo/xml_helpers.jinja', helper_path, base_package=self.base_package)
|
|
421
461
|
|
|
422
462
|
def write_go_mod_file(self):
|
|
423
463
|
"""Writes the go.mod file for the Go project"""
|
|
@@ -453,7 +493,7 @@ class AvroToGo:
|
|
|
453
493
|
self.convert_schema(schema, output_dir)
|
|
454
494
|
|
|
455
495
|
|
|
456
|
-
def convert_avro_to_go(avro_schema_path, go_file_path, package_name='', avro_annotation=False, json_annotation=False, package_site='github.com', package_username='username'):
|
|
496
|
+
def convert_avro_to_go(avro_schema_path, go_file_path, package_name='', avro_annotation=False, json_annotation=False, xml_annotation=False, package_site='github.com', package_username='username'):
|
|
457
497
|
"""Converts Avro schema to Go structs
|
|
458
498
|
|
|
459
499
|
Args:
|
|
@@ -462,6 +502,7 @@ def convert_avro_to_go(avro_schema_path, go_file_path, package_name='', avro_ann
|
|
|
462
502
|
package_name (str): Base package name
|
|
463
503
|
avro_annotation (bool): Include Avro annotations
|
|
464
504
|
json_annotation (bool): Include JSON annotations
|
|
505
|
+
xml_annotation (bool): Include XML annotations and serialization
|
|
465
506
|
"""
|
|
466
507
|
if not package_name:
|
|
467
508
|
package_name = os.path.splitext(os.path.basename(avro_schema_path))[0]
|
|
@@ -469,12 +510,13 @@ def convert_avro_to_go(avro_schema_path, go_file_path, package_name='', avro_ann
|
|
|
469
510
|
avrotogo = AvroToGo(package_name)
|
|
470
511
|
avrotogo.avro_annotation = avro_annotation
|
|
471
512
|
avrotogo.json_annotation = json_annotation
|
|
513
|
+
avrotogo.xml_annotation = xml_annotation
|
|
472
514
|
avrotogo.package_site = package_site if package_site else 'github.com'
|
|
473
515
|
avrotogo.package_username = package_username if package_username else 'username'
|
|
474
516
|
avrotogo.convert(avro_schema_path, go_file_path)
|
|
475
517
|
|
|
476
518
|
|
|
477
|
-
def convert_avro_schema_to_go(avro_schema: JsonNode, output_dir: str, package_name='', avro_annotation=False, json_annotation=False, package_site='github.com', package_username='username'):
|
|
519
|
+
def convert_avro_schema_to_go(avro_schema: JsonNode, output_dir: str, package_name='', avro_annotation=False, json_annotation=False, xml_annotation=False, package_site='github.com', package_username='username'):
|
|
478
520
|
"""Converts Avro schema to Go structs
|
|
479
521
|
|
|
480
522
|
Args:
|
|
@@ -483,10 +525,12 @@ def convert_avro_schema_to_go(avro_schema: JsonNode, output_dir: str, package_na
|
|
|
483
525
|
package_name (str): Base package name
|
|
484
526
|
avro_annotation (bool): Include Avro annotations
|
|
485
527
|
json_annotation (bool): Include JSON annotations
|
|
528
|
+
xml_annotation (bool): Include XML annotations and serialization
|
|
486
529
|
"""
|
|
487
530
|
avrotogo = AvroToGo(package_name)
|
|
488
531
|
avrotogo.avro_annotation = avro_annotation
|
|
489
532
|
avrotogo.json_annotation = json_annotation
|
|
533
|
+
avrotogo.xml_annotation = xml_annotation
|
|
490
534
|
avrotogo.package_site = package_site if package_site else 'github.com'
|
|
491
535
|
avrotogo.package_username = package_username if package_username else 'username'
|
|
492
536
|
avrotogo.convert_schema(avro_schema, output_dir)
|