structurize 2.16.2__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 +63 -0
- avrotize/__main__.py +6 -0
- avrotize/_version.py +34 -0
- avrotize/asn1toavro.py +160 -0
- avrotize/avrotize.py +152 -0
- avrotize/avrotocpp.py +483 -0
- avrotize/avrotocsharp.py +992 -0
- avrotize/avrotocsv.py +121 -0
- avrotize/avrotodatapackage.py +173 -0
- avrotize/avrotodb.py +1383 -0
- avrotize/avrotogo.py +476 -0
- avrotize/avrotographql.py +197 -0
- avrotize/avrotoiceberg.py +210 -0
- avrotize/avrotojava.py +1023 -0
- avrotize/avrotojs.py +250 -0
- avrotize/avrotojsons.py +481 -0
- avrotize/avrotojstruct.py +345 -0
- avrotize/avrotokusto.py +364 -0
- avrotize/avrotomd.py +137 -0
- avrotize/avrotools.py +168 -0
- avrotize/avrotoparquet.py +208 -0
- avrotize/avrotoproto.py +359 -0
- avrotize/avrotopython.py +622 -0
- avrotize/avrotorust.py +435 -0
- avrotize/avrotots.py +598 -0
- avrotize/avrotoxsd.py +344 -0
- avrotize/commands.json +2433 -0
- avrotize/common.py +829 -0
- avrotize/constants.py +5 -0
- avrotize/csvtoavro.py +132 -0
- avrotize/datapackagetoavro.py +76 -0
- avrotize/dependency_resolver.py +348 -0
- avrotize/jsonstoavro.py +1698 -0
- avrotize/jsonstostructure.py +2642 -0
- avrotize/jstructtoavro.py +878 -0
- avrotize/kstructtoavro.py +93 -0
- avrotize/kustotoavro.py +455 -0
- avrotize/parquettoavro.py +157 -0
- avrotize/proto2parser.py +498 -0
- avrotize/proto3parser.py +403 -0
- avrotize/prototoavro.py +382 -0
- avrotize/structuretocsharp.py +2005 -0
- avrotize/structuretojsons.py +498 -0
- avrotize/structuretopython.py +772 -0
- avrotize/xsdtoavro.py +413 -0
- structurize-2.16.2.dist-info/METADATA +805 -0
- structurize-2.16.2.dist-info/RECORD +51 -0
- structurize-2.16.2.dist-info/WHEEL +5 -0
- structurize-2.16.2.dist-info/entry_points.txt +2 -0
- structurize-2.16.2.dist-info/licenses/LICENSE +201 -0
- structurize-2.16.2.dist-info/top_level.txt +1 -0
avrotize/avrotojava.py
ADDED
|
@@ -0,0 +1,1023 @@
|
|
|
1
|
+
# pylint: disable=too-many-arguments, too-many-locals, too-many-branches, too-many-statements, line-too-long
|
|
2
|
+
|
|
3
|
+
""" Generates Java classes from Avro schema """
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
from typing import Dict, List, Tuple, Union
|
|
7
|
+
from avrotize.constants import AVRO_VERSION, JACKSON_VERSION, JDK_VERSION
|
|
8
|
+
|
|
9
|
+
from avrotize.common import pascal, camel, is_generic_avro_type
|
|
10
|
+
|
|
11
|
+
INDENT = ' '
|
|
12
|
+
POM_CONTENT = """<?xml version="1.0" encoding="UTF-8"?>
|
|
13
|
+
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
14
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
15
|
+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
16
|
+
<modelVersion>4.0.0</modelVersion>
|
|
17
|
+
<groupId>{groupid}</groupId>
|
|
18
|
+
<artifactId>{artifactid}</artifactId>
|
|
19
|
+
<version>1.0-SNAPSHOT</version>
|
|
20
|
+
<properties>
|
|
21
|
+
<maven.compiler.source>{JDK_VERSION}</maven.compiler.source>
|
|
22
|
+
<maven.compiler.target>{JDK_VERSION}</maven.compiler.target>
|
|
23
|
+
</properties>
|
|
24
|
+
<dependencies>
|
|
25
|
+
<dependency>
|
|
26
|
+
<groupId>org.apache.avro</groupId>
|
|
27
|
+
<artifactId>avro</artifactId>
|
|
28
|
+
<version>{AVRO_VERSION}</version>
|
|
29
|
+
</dependency>
|
|
30
|
+
<dependency>
|
|
31
|
+
<groupId>com.fasterxml.jackson</groupId>
|
|
32
|
+
<artifactId>jackson-bom</artifactId>
|
|
33
|
+
<version>{JACKSON_VERSION}</version>
|
|
34
|
+
<type>pom</type>
|
|
35
|
+
</dependency>
|
|
36
|
+
</dependencies>
|
|
37
|
+
</project>
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
PREAMBLE_TOBYTEARRAY = \
|
|
41
|
+
"""
|
|
42
|
+
byte[] result = null;
|
|
43
|
+
String mediaType = contentType.split(";")[0].trim().toLowerCase();
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
EPILOGUE_TOBYTEARRAY_COMPRESSION = \
|
|
48
|
+
"""
|
|
49
|
+
if (result != null && mediaType.endsWith("+gzip")) {
|
|
50
|
+
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
|
51
|
+
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream)) {
|
|
52
|
+
gzipOutputStream.write(result);
|
|
53
|
+
gzipOutputStream.finish();
|
|
54
|
+
result = byteArrayOutputStream.toByteArray();
|
|
55
|
+
} catch (IOException e) {
|
|
56
|
+
throw new UnsupportedOperationException("Error compressing data to gzip");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
EPILOGUE_TOBYTEARRAY = \
|
|
62
|
+
"""
|
|
63
|
+
throw new UnsupportedOperationException("Unsupported media type + mediaType");
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
PREAMBLE_FROMDATA_COMPRESSION = \
|
|
67
|
+
"""
|
|
68
|
+
if (mediaType.endsWith("+gzip")) {
|
|
69
|
+
InputStream stream = null;
|
|
70
|
+
|
|
71
|
+
if (data instanceof InputStream) {
|
|
72
|
+
stream = (InputStream) data;
|
|
73
|
+
} else if (data instanceof byte[]) {
|
|
74
|
+
stream = new ByteArrayInputStream((byte[]) data);
|
|
75
|
+
} else {
|
|
76
|
+
throw new UnsupportedOperationException("Data is not of a supported type for gzip decompression");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
try (InputStream gzipStream = new GZIPInputStream(stream);
|
|
80
|
+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
|
81
|
+
byte[] buffer = new byte[1024];
|
|
82
|
+
int bytesRead;
|
|
83
|
+
while ((bytesRead = gzipStream.read(buffer)) != -1) {
|
|
84
|
+
outputStream.write(buffer, 0, bytesRead);
|
|
85
|
+
}
|
|
86
|
+
data = outputStream.toByteArray();
|
|
87
|
+
} catch (IOException e) {
|
|
88
|
+
e.printStackTrace();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
JSON_FROMDATA_THROWS = \
|
|
95
|
+
",JsonProcessingException, IOException"
|
|
96
|
+
JSON_FROMDATA = \
|
|
97
|
+
"""
|
|
98
|
+
if ( mediaType == "application/json") {
|
|
99
|
+
if (data instanceof byte[]) {
|
|
100
|
+
ByteArrayInputStream stream = new ByteArrayInputStream((byte[]) data);
|
|
101
|
+
return (new ObjectMapper()).readValue(stream, {typeName}.class);
|
|
102
|
+
}
|
|
103
|
+
else if (data instanceof InputStream) {
|
|
104
|
+
return (new ObjectMapper()).readValue((InputStream)data, {typeName}.class);
|
|
105
|
+
}
|
|
106
|
+
else if (data instanceof JsonNode) {
|
|
107
|
+
return (new ObjectMapper()).readValue(((JsonNode)data).toString(), {typeName}.class);
|
|
108
|
+
}
|
|
109
|
+
else if ( data instanceof String) {
|
|
110
|
+
return (new ObjectMapper()).readValue(((String)data), {typeName}.class);
|
|
111
|
+
}
|
|
112
|
+
throw new UnsupportedOperationException("Data is not of a supported type for JSON conversion to {typeName}");
|
|
113
|
+
}
|
|
114
|
+
"""
|
|
115
|
+
JSON_TOBYTEARRAY_THROWS = ",JsonProcessingException"
|
|
116
|
+
JSON_TOBYTEARRAY = \
|
|
117
|
+
"""
|
|
118
|
+
if ( mediaType == "application/json") {
|
|
119
|
+
result = new ObjectMapper().writeValueAsBytes(this);
|
|
120
|
+
}
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
AVRO_FROMDATA_THROWS = ",IOException"
|
|
124
|
+
AVRO_FROMDATA = \
|
|
125
|
+
"""
|
|
126
|
+
if ( mediaType == "avro/binary" || mediaType == "application/vnd.apache.avro+avro") {
|
|
127
|
+
if (data instanceof byte[]) {
|
|
128
|
+
return AVROREADER.read(new {typeName}(), DecoderFactory.get().binaryDecoder((byte[])data, null));
|
|
129
|
+
} else if (data instanceof InputStream) {
|
|
130
|
+
return AVROREADER.read(new {typeName}(), DecoderFactory.get().binaryDecoder((InputStream)data, null));
|
|
131
|
+
}
|
|
132
|
+
throw new UnsupportedOperationException("Data is not of a supported type for Avro conversion to {typeName}");
|
|
133
|
+
} else if ( mediaType == "avro/json" || mediaType == "application/vnd.apache.avro+json") {
|
|
134
|
+
if (data instanceof byte[]) {
|
|
135
|
+
return AVROREADER.read(new {typeName}(), DecoderFactory.get().jsonDecoder({typeName}.AVROSCHEMA, new ByteArrayInputStream((byte[])data)));
|
|
136
|
+
} else if (data instanceof InputStream) {
|
|
137
|
+
return AVROREADER.read(new {typeName}(), DecoderFactory.get().jsonDecoder({typeName}.AVROSCHEMA, (InputStream)data));
|
|
138
|
+
} else if (data instanceof String) {
|
|
139
|
+
return AVROREADER.read(new {typeName}(), DecoderFactory.get().jsonDecoder({typeName}.AVROSCHEMA, (String)data));
|
|
140
|
+
}
|
|
141
|
+
throw new UnsupportedOperationException("Data is not of a supported type for Avro conversion to {typeName}");
|
|
142
|
+
}
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
AVRO_TOBYTEARRAY_THROWS = ",IOException"
|
|
147
|
+
AVRO_TOBYTEARRAY = \
|
|
148
|
+
"""
|
|
149
|
+
if ( mediaType == "avro/binary" || mediaType == "application/vnd.apache.avro+avro") {
|
|
150
|
+
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
151
|
+
Encoder encoder = EncoderFactory.get().binaryEncoder(out, null);
|
|
152
|
+
AVROWRITER.write(this, encoder);
|
|
153
|
+
encoder.flush();
|
|
154
|
+
result = out.toByteArray();
|
|
155
|
+
}
|
|
156
|
+
else if ( mediaType == "avro/json" || mediaType == "application/vnd.apache.avro+json") {
|
|
157
|
+
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
158
|
+
Encoder encoder = EncoderFactory.get().jsonEncoder({typeName}.AVROSCHEMA, out);
|
|
159
|
+
AVROWRITER.write(this, encoder);
|
|
160
|
+
encoder.flush();
|
|
161
|
+
result = out.toByteArray();
|
|
162
|
+
}
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
JsonNode = Dict[str, 'JsonNode'] | List['JsonNode'] | str | None
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def flatten_type_name(name: str) -> str:
|
|
170
|
+
"""Strips the namespace from a name"""
|
|
171
|
+
if name.endswith('[]'):
|
|
172
|
+
return flatten_type_name(name[:-2]+'Array')
|
|
173
|
+
base_name = pascal(name.replace(' ', '').split('.')[-1].replace('>', '').replace('<', '').replace(',', ''))
|
|
174
|
+
return base_name
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def is_java_reserved_word(word: str) -> bool:
|
|
178
|
+
"""Checks if a word is a Java reserved word"""
|
|
179
|
+
reserved_words = [
|
|
180
|
+
'abstract', 'assert', 'boolean', 'break', 'byte', 'case', 'catch', 'char', 'class', 'const',
|
|
181
|
+
'continue', 'default', 'do', 'double', 'else', 'enum', 'extends', 'final', 'finally', 'float',
|
|
182
|
+
'for', 'goto', 'if', 'implements', 'import', 'instanceof', 'int', 'interface', 'long', 'native',
|
|
183
|
+
'new', 'package', 'private', 'protected', 'public', 'return', 'short', 'static', 'strictfp',
|
|
184
|
+
'super', 'switch', 'synchronized', 'this', 'throw', 'throws', 'transient', 'try', 'void', 'volatile',
|
|
185
|
+
'while', 'true', 'false', 'null', 'record',
|
|
186
|
+
]
|
|
187
|
+
return word in reserved_words
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class AvroToJava:
|
|
191
|
+
"""Converts Avro schema to Java classes, including Jackson annotations and Avro SpecificRecord methods"""
|
|
192
|
+
|
|
193
|
+
def __init__(self, base_package: str = '') -> None:
|
|
194
|
+
self.base_package = base_package.replace('.', '/')
|
|
195
|
+
self.output_dir = os.getcwd()
|
|
196
|
+
self.avro_annotation = False
|
|
197
|
+
self.jackson_annotations = False
|
|
198
|
+
self.pascal_properties = False
|
|
199
|
+
self.generated_types_avro_namespace: Dict[str,str] = {}
|
|
200
|
+
self.generated_types_java_package: Dict[str,str] = {}
|
|
201
|
+
|
|
202
|
+
def qualified_name(self, package: str, name: str) -> str:
|
|
203
|
+
"""Concatenates package and name using a dot separator"""
|
|
204
|
+
slash_package_name = package.replace('.', '/')
|
|
205
|
+
safe_package_slash = self.safe_package(slash_package_name.lower())
|
|
206
|
+
safe_package = safe_package_slash.replace('/', '.')
|
|
207
|
+
return f"{safe_package}.{name}" if package else name
|
|
208
|
+
|
|
209
|
+
def join_packages(self, parent_package: str, package: str) -> str:
|
|
210
|
+
"""Joins package and name using a dot separator"""
|
|
211
|
+
if parent_package and package:
|
|
212
|
+
return f"{parent_package}.{package}".lower()
|
|
213
|
+
elif parent_package:
|
|
214
|
+
return parent_package.lower()
|
|
215
|
+
return package.lower()
|
|
216
|
+
|
|
217
|
+
class JavaType:
|
|
218
|
+
"""Java type definition"""
|
|
219
|
+
|
|
220
|
+
def __init__(self, type_name: str, union_types: List['AvroToJava.JavaType'] | None = None, is_class: bool = False, is_enum: bool = False) -> None:
|
|
221
|
+
self.type_name = type_name
|
|
222
|
+
self.union_types = union_types
|
|
223
|
+
self.is_class = is_class
|
|
224
|
+
self.is_enum = is_enum
|
|
225
|
+
|
|
226
|
+
def safe_identifier(self, name: str, class_name: str = '') -> str:
|
|
227
|
+
"""Converts a name to a safe Java identifier"""
|
|
228
|
+
if is_java_reserved_word(name):
|
|
229
|
+
return f"_{name}"
|
|
230
|
+
if class_name and name == class_name:
|
|
231
|
+
return f"{name}_"
|
|
232
|
+
return name
|
|
233
|
+
|
|
234
|
+
def safe_package(self, packageName: str) -> str:
|
|
235
|
+
"""Converts a name to a safe Java identifier by checking each path segment"""
|
|
236
|
+
segments = packageName.split('/')
|
|
237
|
+
safe_segments = [
|
|
238
|
+
self.safe_identifier(segment)
|
|
239
|
+
for segment in segments
|
|
240
|
+
]
|
|
241
|
+
|
|
242
|
+
return '/'.join(safe_segments)
|
|
243
|
+
|
|
244
|
+
def map_primitive_to_java(self, avro_type: str, is_optional: bool) -> JavaType:
|
|
245
|
+
"""Maps Avro primitive types to Java types"""
|
|
246
|
+
optional_mapping = {
|
|
247
|
+
'null': 'Void',
|
|
248
|
+
'boolean': 'Boolean',
|
|
249
|
+
'int': 'Integer',
|
|
250
|
+
'long': 'Long',
|
|
251
|
+
'float': 'Float',
|
|
252
|
+
'double': 'Double',
|
|
253
|
+
'bytes': 'byte[]',
|
|
254
|
+
'string': 'String',
|
|
255
|
+
}
|
|
256
|
+
required_mapping = {
|
|
257
|
+
'null': 'void',
|
|
258
|
+
'boolean': 'boolean',
|
|
259
|
+
'int': 'int',
|
|
260
|
+
'long': 'long',
|
|
261
|
+
'float': 'float',
|
|
262
|
+
'double': 'double',
|
|
263
|
+
'bytes': 'byte[]',
|
|
264
|
+
'string': 'String',
|
|
265
|
+
}
|
|
266
|
+
if '.' in avro_type:
|
|
267
|
+
type_name = avro_type.split('.')[-1]
|
|
268
|
+
package_name = '.'.join(avro_type.split('.')[:-1]).lower()
|
|
269
|
+
avro_type = self.qualified_name(package_name, type_name)
|
|
270
|
+
if avro_type in self.generated_types_avro_namespace:
|
|
271
|
+
kind = self.generated_types_avro_namespace[avro_type]
|
|
272
|
+
qualified_class_name = self.qualified_name(self.base_package, avro_type)
|
|
273
|
+
return AvroToJava.JavaType(qualified_class_name, is_class=kind=="class", is_enum=kind=="enum")
|
|
274
|
+
else:
|
|
275
|
+
return AvroToJava.JavaType(required_mapping.get(avro_type, avro_type) if not is_optional else optional_mapping.get(avro_type, avro_type))
|
|
276
|
+
|
|
277
|
+
def is_java_primitive(self, java_type: JavaType) -> bool:
|
|
278
|
+
"""Checks if a Java type is a primitive type"""
|
|
279
|
+
return java_type.type_name in [
|
|
280
|
+
'void', 'boolean', 'int', 'long', 'float', 'double', 'byte[]', 'String',
|
|
281
|
+
'Boolean', 'Integer', 'Long', 'Float', 'Double', 'Void']
|
|
282
|
+
|
|
283
|
+
def is_java_optional_type(self, java_type: JavaType) -> bool:
|
|
284
|
+
"""Checks if a Java type is an optional type"""
|
|
285
|
+
return java_type.type_name in ['Void', 'Boolean', 'Integer', 'Long', 'Float', 'Double']
|
|
286
|
+
|
|
287
|
+
def is_java_numeric_type(self, java_type: JavaType) -> bool:
|
|
288
|
+
"""Checks if a Java type is a numeric type"""
|
|
289
|
+
return java_type.type_name in ['int', 'long', 'float', 'double', 'Integer', 'Long', 'Float', 'Double']
|
|
290
|
+
|
|
291
|
+
def is_java_integer_type(self, java_type: JavaType) -> bool:
|
|
292
|
+
"""Checks if a Java type is an integer type"""
|
|
293
|
+
return java_type.type_name in ['int', 'long', 'Integer', 'Long']
|
|
294
|
+
|
|
295
|
+
def convert_avro_type_to_java(self, class_name: str, field_name: str, avro_type: Union[str, Dict, List], parent_package: str, nullable: bool = False) -> JavaType:
|
|
296
|
+
"""Converts Avro type to Java type"""
|
|
297
|
+
if isinstance(avro_type, str):
|
|
298
|
+
return self.map_primitive_to_java(avro_type, nullable)
|
|
299
|
+
elif isinstance(avro_type, list):
|
|
300
|
+
if (is_generic_avro_type(avro_type)):
|
|
301
|
+
return AvroToJava.JavaType('Object')
|
|
302
|
+
non_null_types = [t for t in avro_type if t != 'null']
|
|
303
|
+
if len(non_null_types) == 1:
|
|
304
|
+
if isinstance(non_null_types[0], str):
|
|
305
|
+
return self.map_primitive_to_java(non_null_types[0], True)
|
|
306
|
+
else:
|
|
307
|
+
return self.convert_avro_type_to_java(class_name, field_name, non_null_types[0], parent_package)
|
|
308
|
+
else:
|
|
309
|
+
if self.jackson_annotations:
|
|
310
|
+
return AvroToJava.JavaType(self.generate_embedded_union_class_jackson(class_name, field_name, non_null_types, parent_package, write_file=True), is_class=True)
|
|
311
|
+
else:
|
|
312
|
+
types: List[AvroToJava.JavaType] = [self.convert_avro_type_to_java(
|
|
313
|
+
class_name, field_name, t, parent_package) for t in non_null_types]
|
|
314
|
+
return AvroToJava.JavaType('Object', types)
|
|
315
|
+
elif isinstance(avro_type, dict):
|
|
316
|
+
if avro_type['type'] in ['record', 'enum']:
|
|
317
|
+
return self.generate_class_or_enum(avro_type, parent_package, write_file=True)
|
|
318
|
+
elif avro_type['type'] == 'fixed':
|
|
319
|
+
if 'logicalType' in avro_type and avro_type['logicalType'] == 'decimal':
|
|
320
|
+
return AvroToJava.JavaType('BigDecimal')
|
|
321
|
+
return AvroToJava.JavaType('byte[]')
|
|
322
|
+
elif avro_type['type'] == 'bytes' and 'logicalType' in avro_type:
|
|
323
|
+
if avro_type['logicalType'] == 'decimal':
|
|
324
|
+
return AvroToJava.JavaType('BigDecimal')
|
|
325
|
+
elif avro_type['type'] == 'array':
|
|
326
|
+
item_type = self.convert_avro_type_to_java(class_name, field_name, avro_type['items'], parent_package, nullable=True).type_name
|
|
327
|
+
return AvroToJava.JavaType(f"List<{item_type}>")
|
|
328
|
+
elif avro_type['type'] == 'map':
|
|
329
|
+
values_type = self.convert_avro_type_to_java(class_name, field_name, avro_type['values'], parent_package, nullable=True).type_name
|
|
330
|
+
return AvroToJava.JavaType(f"Map<String,{values_type}>")
|
|
331
|
+
elif 'logicalType' in avro_type:
|
|
332
|
+
if avro_type['logicalType'] == 'date':
|
|
333
|
+
return AvroToJava.JavaType('LocalDate')
|
|
334
|
+
elif avro_type['logicalType'] == 'time-millis' or avro_type['logicalType'] == 'time-micros':
|
|
335
|
+
return AvroToJava.JavaType('LocalTime')
|
|
336
|
+
elif avro_type['logicalType'] == 'timestamp-millis' or avro_type['logicalType'] == 'timestamp-micros':
|
|
337
|
+
return AvroToJava.JavaType('Instant')
|
|
338
|
+
elif avro_type['logicalType'] == 'local-timestamp-millis' or avro_type['logicalType'] == 'local-timestamp-micros':
|
|
339
|
+
return AvroToJava.JavaType('LocalDateTime')
|
|
340
|
+
elif avro_type['logicalType'] == 'uuid':
|
|
341
|
+
return AvroToJava.JavaType('UUID')
|
|
342
|
+
elif avro_type['logicalType'] == 'duration':
|
|
343
|
+
return AvroToJava.JavaType('Duration')
|
|
344
|
+
return self.convert_avro_type_to_java(class_name, field_name, avro_type['type'], parent_package)
|
|
345
|
+
return 'Object'
|
|
346
|
+
|
|
347
|
+
def generate_class_or_enum(self, avro_schema: Dict, parent_package: str, write_file: bool = True) -> JavaType:
|
|
348
|
+
""" Generates a Java class or enum from an Avro schema """
|
|
349
|
+
if avro_schema['type'] == 'record':
|
|
350
|
+
return self.generate_class(avro_schema, parent_package, write_file)
|
|
351
|
+
elif avro_schema['type'] == 'enum':
|
|
352
|
+
return self.generate_enum(avro_schema, parent_package, write_file)
|
|
353
|
+
return AvroToJava.JavaType('Object')
|
|
354
|
+
|
|
355
|
+
def generate_class(self, avro_schema: Dict, parent_package: str, write_file: bool) -> JavaType:
|
|
356
|
+
""" Generates a Java class from an Avro record schema """
|
|
357
|
+
class_definition = ''
|
|
358
|
+
if 'doc' in avro_schema:
|
|
359
|
+
class_definition += f"/** {avro_schema['doc']} */\n"
|
|
360
|
+
namespace = avro_schema.get('namespace', parent_package)
|
|
361
|
+
if not 'namespace' in avro_schema:
|
|
362
|
+
avro_schema['namespace'] = namespace
|
|
363
|
+
package = self.join_packages(self.base_package, namespace).replace('.', '/').lower()
|
|
364
|
+
package = package.replace('.', '/').lower()
|
|
365
|
+
package = self.safe_package(package)
|
|
366
|
+
class_name = self.safe_identifier(avro_schema['name'])
|
|
367
|
+
namespace_qualified_name = self.qualified_name(namespace,avro_schema['name'])
|
|
368
|
+
qualified_class_name = self.qualified_name(package.replace('/', '.'), class_name)
|
|
369
|
+
if namespace_qualified_name in self.generated_types_avro_namespace:
|
|
370
|
+
return AvroToJava.JavaType(qualified_class_name, is_class=True)
|
|
371
|
+
self.generated_types_avro_namespace[namespace_qualified_name] = "class"
|
|
372
|
+
self.generated_types_java_package[qualified_class_name] = "class"
|
|
373
|
+
fields_str = [self.generate_property(class_name, field, namespace) for field in avro_schema.get('fields', [])]
|
|
374
|
+
class_body = "\n".join(fields_str)
|
|
375
|
+
class_definition += f"public class {class_name}"
|
|
376
|
+
if self.avro_annotation:
|
|
377
|
+
class_definition += " implements SpecificRecord"
|
|
378
|
+
class_definition += " {\n"
|
|
379
|
+
class_definition += f"{INDENT}public {class_name}() {{}}\n"
|
|
380
|
+
class_definition += class_body
|
|
381
|
+
|
|
382
|
+
if self.avro_annotation:
|
|
383
|
+
class_definition += f"\n{INDENT}public {class_name}(GenericData.Record record) {{\n"
|
|
384
|
+
class_definition += f"{INDENT*2}for( int i = 0; i < record.getSchema().getFields().size(); i++ ) {{\n"
|
|
385
|
+
class_definition += f"{INDENT*3}this.put(i, record.get(i));\n"
|
|
386
|
+
class_definition += f"{INDENT*2}}}\n"
|
|
387
|
+
class_definition += f"{INDENT}}}\n"
|
|
388
|
+
|
|
389
|
+
if self.avro_annotation:
|
|
390
|
+
avro_schema_json = json.dumps(avro_schema)
|
|
391
|
+
avro_schema_json = avro_schema_json.replace('"', '§')
|
|
392
|
+
avro_schema_json = f"\"+\n{INDENT}\"".join(
|
|
393
|
+
[avro_schema_json[i:i+80] for i in range(0, len(avro_schema_json), 80)])
|
|
394
|
+
avro_schema_json = avro_schema_json.replace('§', '\\"')
|
|
395
|
+
class_definition += f"\n\n{INDENT}public static final Schema AVROSCHEMA = new Schema.Parser().parse(\n{INDENT}\"{avro_schema_json}\");"
|
|
396
|
+
class_definition += f"\n{INDENT}public static final DatumWriter<{class_name}> AVROWRITER = new SpecificDatumWriter<{class_name}>(AVROSCHEMA);"
|
|
397
|
+
class_definition += f"\n{INDENT}public static final DatumReader<{class_name}> AVROREADER = new SpecificDatumReader<{class_name}>(AVROSCHEMA);\n"
|
|
398
|
+
|
|
399
|
+
if self.jackson_annotations:
|
|
400
|
+
class_definition += f"\n{INDENT}@JsonIgnore"
|
|
401
|
+
class_definition += f"\n{INDENT}@Override\n{INDENT}public Schema getSchema(){{ return AVROSCHEMA; }}\n"
|
|
402
|
+
class_definition += self.generate_avro_get_method(class_name, avro_schema.get('fields', []), namespace)
|
|
403
|
+
class_definition += self.generate_avro_put_method(class_name, avro_schema.get('fields', []), namespace)
|
|
404
|
+
|
|
405
|
+
# emit toByteArray method
|
|
406
|
+
class_definition += f"\n\n{INDENT}/**\n{INDENT} * Converts the object to a byte array\n{INDENT} * @param contentType the content type of the byte array\n{INDENT} * @return the byte array\n{INDENT} */\n"
|
|
407
|
+
class_definition += f"{INDENT}public byte[] toByteArray(String contentType) throws UnsupportedOperationException" + \
|
|
408
|
+
f"{ JSON_TOBYTEARRAY_THROWS if self.jackson_annotations else '' }" + \
|
|
409
|
+
f"{ AVRO_TOBYTEARRAY_THROWS if self.avro_annotation else '' } {{"
|
|
410
|
+
if self.jackson_annotations or self.avro_annotation:
|
|
411
|
+
class_definition += f'\n{INDENT*2}'.join((PREAMBLE_TOBYTEARRAY).split("\n"))
|
|
412
|
+
if self.avro_annotation:
|
|
413
|
+
class_definition += f'\n{INDENT*2}'+f'\n{INDENT*2}'.join(
|
|
414
|
+
AVRO_TOBYTEARRAY.strip().replace("{typeName}", class_name).split("\n"))
|
|
415
|
+
if self.jackson_annotations:
|
|
416
|
+
class_definition += f'\n{INDENT*2}'+f'\n{INDENT*2}'.join(
|
|
417
|
+
JSON_TOBYTEARRAY.strip().replace("{typeName}", class_name).split("\n"))
|
|
418
|
+
if self.avro_annotation or self.jackson_annotations:
|
|
419
|
+
class_definition += f'\n{INDENT*2}'.join(EPILOGUE_TOBYTEARRAY_COMPRESSION.split("\n"))
|
|
420
|
+
class_definition += f'\n{INDENT*2}if ( result != null ) {{ return result; }}'
|
|
421
|
+
class_definition += (f'\n{INDENT*2}'.join((EPILOGUE_TOBYTEARRAY.strip()).split("\n")))+f"\n{INDENT}}}"
|
|
422
|
+
|
|
423
|
+
# emit fromData factory method
|
|
424
|
+
class_definition += f"\n\n{INDENT}/**\n{INDENT} * Converts the data to an object\n{INDENT} * @param data the data to convert\n{INDENT} * @param contentType the content type of the data\n{INDENT} * @return the object\n{INDENT} */\n"
|
|
425
|
+
class_definition += f"{INDENT}public static {class_name} fromData(Object data, String contentType) throws UnsupportedOperationException" + \
|
|
426
|
+
f"{ JSON_FROMDATA_THROWS if self.jackson_annotations else '' }" + \
|
|
427
|
+
f"{ AVRO_FROMDATA_THROWS if self.avro_annotation else '' } {{"
|
|
428
|
+
class_definition += f'\n{INDENT*2}if ( data instanceof {class_name}) return ({class_name})data;'
|
|
429
|
+
|
|
430
|
+
if self.avro_annotation or self.jackson_annotations:
|
|
431
|
+
class_definition += f'\n{INDENT*2}String mediaType = contentType.split(";")[0].trim().toLowerCase();'
|
|
432
|
+
class_definition += f'\n{INDENT*2}'.join((PREAMBLE_FROMDATA_COMPRESSION).split("\n"))
|
|
433
|
+
if self.avro_annotation:
|
|
434
|
+
class_definition += f'\n{INDENT*2}'+f'\n{INDENT*2}'.join(
|
|
435
|
+
AVRO_FROMDATA.strip().replace("{typeName}", class_name).split("\n"))
|
|
436
|
+
if self.jackson_annotations:
|
|
437
|
+
class_definition += f'\n{INDENT*2}'+f'\n{INDENT*2}'.join(
|
|
438
|
+
JSON_FROMDATA.strip().replace("{typeName}", class_name).split("\n"))
|
|
439
|
+
class_definition += f"\n{INDENT*2}throw new UnsupportedOperationException(\"Unsupported media type \"+ contentType);\n{INDENT}}}"
|
|
440
|
+
|
|
441
|
+
if self.jackson_annotations:
|
|
442
|
+
class_definition += self.create_is_json_match_method(avro_schema, avro_schema.get('namespace', namespace), class_name)
|
|
443
|
+
|
|
444
|
+
class_definition += "\n}"
|
|
445
|
+
|
|
446
|
+
if write_file:
|
|
447
|
+
self.write_to_file(package, class_name, class_definition)
|
|
448
|
+
return AvroToJava.JavaType(qualified_class_name, is_class=True)
|
|
449
|
+
|
|
450
|
+
def create_is_json_match_method(self, avro_schema, parent_namespace, class_name) -> str:
|
|
451
|
+
""" Generates the isJsonMatch method for a class using Jackson """
|
|
452
|
+
predicates = ''
|
|
453
|
+
class_definition = ''
|
|
454
|
+
class_definition += f"\n\n{INDENT}/**\n{INDENT} * Checks if the JSON node matches the schema\n{INDENT}"
|
|
455
|
+
class_definition += f"\n{INDENT}@param node The JSON node to check */"
|
|
456
|
+
class_definition += f"\n{INDENT}public static boolean isJsonMatch(com.fasterxml.jackson.databind.JsonNode node)\n{INDENT}{{"
|
|
457
|
+
field_defs = ''
|
|
458
|
+
|
|
459
|
+
field_count = 0
|
|
460
|
+
for field in avro_schema.get('fields', []):
|
|
461
|
+
if field_count > 0:
|
|
462
|
+
field_defs += f" && \n{INDENT*3}"
|
|
463
|
+
field_count += 1
|
|
464
|
+
field_name = field['name']
|
|
465
|
+
if field_name == class_name:
|
|
466
|
+
field_name += "_"
|
|
467
|
+
field_type = self.convert_avro_type_to_java(class_name, field_name, field['type'], parent_namespace)
|
|
468
|
+
predicate, clause = self.get_is_json_match_clause(class_name, field_name, field_type)
|
|
469
|
+
field_defs += clause
|
|
470
|
+
if predicate:
|
|
471
|
+
predicates += predicate + "\n"
|
|
472
|
+
if ( len(predicates) > 0 ):
|
|
473
|
+
class_definition += f'\n{INDENT*2}'+f'\n{INDENT*2}'.join(predicates.split('\n'))
|
|
474
|
+
class_definition += f"\n{INDENT*2}return {field_defs}"
|
|
475
|
+
class_definition += f";\n{INDENT}}}"
|
|
476
|
+
return class_definition
|
|
477
|
+
|
|
478
|
+
def get_is_json_match_clause(self, class_name: str, field_name: str, field_type: JavaType) -> Tuple[str, str]:
|
|
479
|
+
""" Generates the isJsonMatch clause for a field using Jackson """
|
|
480
|
+
class_definition = ''
|
|
481
|
+
predicates = ''
|
|
482
|
+
field_name_js = field_name
|
|
483
|
+
is_optional = self.is_java_optional_type(field_type)
|
|
484
|
+
|
|
485
|
+
if is_optional:
|
|
486
|
+
node_check = f"!node.has(\"{field_name_js}\") || node.get(\"{field_name_js}\").isNull() || node.get(\"{field_name_js}\")"
|
|
487
|
+
else:
|
|
488
|
+
node_check = f"node.has(\"{field_name_js}\") && node.get(\"{field_name_js}\")"
|
|
489
|
+
|
|
490
|
+
if field_type.type_name == 'byte[]':
|
|
491
|
+
class_definition += f"({node_check}.isBinary())"
|
|
492
|
+
elif field_type.type_name == 'string' or field_type.type_name == 'String':
|
|
493
|
+
class_definition += f"({node_check}.isTextual())"
|
|
494
|
+
elif field_type.type_name == 'int' or field_type.type_name == 'Integer':
|
|
495
|
+
class_definition += f"({node_check}.canConvertToInt())"
|
|
496
|
+
elif field_type.type_name == 'long' or field_type.type_name == 'Long':
|
|
497
|
+
class_definition += f"({node_check}.canConvertToLong())"
|
|
498
|
+
elif field_type.type_name == 'float' or field_type.type_name == 'Float':
|
|
499
|
+
class_definition += f"({node_check}.isFloat())"
|
|
500
|
+
elif field_type.type_name == 'double' or field_type.type_name == 'Double':
|
|
501
|
+
class_definition += f"({node_check}.isDouble())"
|
|
502
|
+
elif field_type.type_name == 'BigDecimal':
|
|
503
|
+
class_definition += f"({node_check}.isBigDecimal())"
|
|
504
|
+
elif field_type.type_name == 'boolean' or field_type.type_name == 'Boolean':
|
|
505
|
+
class_definition += f"({node_check}.isBoolean())"
|
|
506
|
+
elif field_type.type_name == 'UUID':
|
|
507
|
+
class_definition += f"({node_check}.isTextual())"
|
|
508
|
+
elif field_type.type_name == 'LocalDate':
|
|
509
|
+
class_definition += f"({node_check}.isTextual())"
|
|
510
|
+
elif field_type.type_name == 'LocalTime':
|
|
511
|
+
class_definition += f"({node_check}.isTextual())"
|
|
512
|
+
elif field_type.type_name == 'Instant':
|
|
513
|
+
class_definition += f"({node_check}.isTextual())"
|
|
514
|
+
elif field_type.type_name == 'LocalDateTime':
|
|
515
|
+
class_definition += f"({node_check}.isTextual())"
|
|
516
|
+
elif field_type.type_name == 'Duration':
|
|
517
|
+
class_definition += f"({node_check}.isTextual())"
|
|
518
|
+
elif field_type.type_name == "Object":
|
|
519
|
+
class_definition += f"({node_check}.isObject())"
|
|
520
|
+
elif field_type.type_name.startswith("List<"):
|
|
521
|
+
items_type = field_type.type_name[5:-1]
|
|
522
|
+
pred = f"Predicate<JsonNode> val{field_name_js} = (JsonNode n) -> n.isArray() && !n.elements().hasNext() || "
|
|
523
|
+
pred_test = self.predicate_test(items_type)
|
|
524
|
+
if pred_test:
|
|
525
|
+
pred += "n.elements().next()" + pred_test
|
|
526
|
+
elif items_type in self.generated_types_java_package:
|
|
527
|
+
kind = self.generated_types_java_package[items_type]
|
|
528
|
+
if kind == "enum":
|
|
529
|
+
pred += f"n.elements().next().isTextual() && Enum.valueOf({items_type}.class, n.elements().next().asText()) != null"
|
|
530
|
+
else:
|
|
531
|
+
pred += f"{items_type}.isJsonMatch(n.elements().next())"
|
|
532
|
+
else:
|
|
533
|
+
pred += "true"
|
|
534
|
+
predicates += pred + ";"
|
|
535
|
+
class_definition += f"(node.has(\"{field_name_js}\") && val{field_name_js}.test(node.get(\"{field_name_js}\")))"
|
|
536
|
+
elif field_type.type_name.startswith("Map<"):
|
|
537
|
+
comma_offset = field_type.type_name.find(',')+1
|
|
538
|
+
values_type = field_type.type_name[comma_offset:-1]
|
|
539
|
+
pred = f"Predicate<JsonNode> val{field_name_js} = (JsonNode n) -> n.isObject() && !n.elements().hasNext() || "
|
|
540
|
+
pred_test = self.predicate_test(values_type)
|
|
541
|
+
if pred_test:
|
|
542
|
+
pred += "n.elements().next()" + pred_test
|
|
543
|
+
elif values_type in self.generated_types_java_package:
|
|
544
|
+
kind = self.generated_types_java_package[values_type]
|
|
545
|
+
if kind == "enum":
|
|
546
|
+
pred += f"n.elements().next().isTextual() && Enum.valueOf({values_type}.class, n.elements().next().asText()) != null"
|
|
547
|
+
else:
|
|
548
|
+
pred += f"{values_type}.isJsonMatch(n.elements().next())"
|
|
549
|
+
else:
|
|
550
|
+
pred += "true"
|
|
551
|
+
predicates += pred + ";"
|
|
552
|
+
class_definition += f"(node.has(\"{field_name_js}\") && val{field_name_js}.test(node.get(\"{field_name_js}\")))"
|
|
553
|
+
elif field_type.is_class:
|
|
554
|
+
class_definition += f"(node.has(\"{field_name_js}\") && {field_type.type_name}.isJsonMatch(node.get(\"{field_name_js}\")))"
|
|
555
|
+
elif field_type.is_enum:
|
|
556
|
+
class_definition += f"(node.get(\"{field_name_js}\").isTextual() && Enum.valueOf({field_type.type_name}.class, node.get(\"{field_name_js}\").asText()) != null)"
|
|
557
|
+
else:
|
|
558
|
+
is_union = False
|
|
559
|
+
field_union = pascal(field_name) + 'Union'
|
|
560
|
+
if field_type == field_union:
|
|
561
|
+
field_union = class_name + "." + pascal(field_name) + 'Union'
|
|
562
|
+
type_kind = self.generated_types_avro_namespace[field_union] if field_union in self.generated_types_avro_namespace else "class"
|
|
563
|
+
if type_kind == "union":
|
|
564
|
+
is_union = True
|
|
565
|
+
class_definition += f"({node_check}.isObject() && {field_type.type_name}.isJsonMatch(node.get(\"{field_name_js}\")))"
|
|
566
|
+
if not is_union:
|
|
567
|
+
class_definition += f"(node.has(\"{field_name_js}\"))"
|
|
568
|
+
return predicates, class_definition
|
|
569
|
+
|
|
570
|
+
def predicate_test(self, items_type):
|
|
571
|
+
""" Generates the predicate test for a list or map"""
|
|
572
|
+
if items_type == "String":
|
|
573
|
+
return ".isTextual()"
|
|
574
|
+
elif items_type in ['int', 'Integer']:
|
|
575
|
+
return ".canConvertToInt()"
|
|
576
|
+
elif items_type in ['long', 'Long']:
|
|
577
|
+
return ".canConvertToLong()"
|
|
578
|
+
elif items_type in ['float', 'Float', 'double', 'Double', 'decimal']:
|
|
579
|
+
return ".isNumber()"
|
|
580
|
+
elif items_type in ['boolean', 'Boolean']:
|
|
581
|
+
return ".isBoolean()"
|
|
582
|
+
elif items_type == 'byte[]':
|
|
583
|
+
return ".isBinary()"
|
|
584
|
+
elif items_type == 'UUID':
|
|
585
|
+
return ".isTextual()"
|
|
586
|
+
elif items_type == 'LocalDate':
|
|
587
|
+
return ".isTextual()"
|
|
588
|
+
elif items_type == 'LocalTime':
|
|
589
|
+
return ".isTextual()"
|
|
590
|
+
elif items_type == 'Instant':
|
|
591
|
+
return ".isTextual()"
|
|
592
|
+
elif items_type == 'LocalDateTime':
|
|
593
|
+
return ".isTextual()"
|
|
594
|
+
elif items_type == 'Duration':
|
|
595
|
+
return ".isTextual()"
|
|
596
|
+
elif items_type == "Object":
|
|
597
|
+
return ".isObject()"
|
|
598
|
+
return ""
|
|
599
|
+
|
|
600
|
+
def get_is_json_match_clause_type(self, element_name: str, class_name: str, field_type: JavaType) -> str:
|
|
601
|
+
""" Generates the isJsonMatch clause for a field using Jackson """
|
|
602
|
+
predicates = ''
|
|
603
|
+
class_definition = ''
|
|
604
|
+
is_optional = field_type.type_name[-1] == '?'
|
|
605
|
+
#is_optional = field_type[-1] == '?'
|
|
606
|
+
#field_type = field_type[:-1] if is_optional else field_type
|
|
607
|
+
is_optional = False
|
|
608
|
+
node_check = f"{element_name}.isMissingNode() == false && {element_name}"
|
|
609
|
+
null_check = f"{element_name}.isNull()" if is_optional else "false"
|
|
610
|
+
if field_type.type_name == 'byte[]':
|
|
611
|
+
class_definition += f"({node_check}.isBinary()){f' || {null_check}' if is_optional else ''}"
|
|
612
|
+
elif field_type.type_name == 'String':
|
|
613
|
+
class_definition += f"({node_check}.isTextual()){f' || {null_check}' if is_optional else ''}"
|
|
614
|
+
elif self.is_java_numeric_type(field_type):
|
|
615
|
+
class_definition += f"({node_check}.isNumber()){f' || {null_check}' if is_optional else ''}"
|
|
616
|
+
elif field_type.type_name == 'bool' or field_type.type_name == 'Boolean':
|
|
617
|
+
class_definition += f"({node_check}.isBoolean()){f' || {null_check}' if is_optional else ''}"
|
|
618
|
+
elif field_type.type_name.startswith("List<"):
|
|
619
|
+
items_type = field_type.type_name[5:-1]
|
|
620
|
+
predicates += f"Predicate<JsonNode> val{element_name}. = (JsonNode n) -> n.isObject() && n.fields().hasNext() && n.fields().next().getValue().isTextual();"
|
|
621
|
+
class_definition += f"({node_check}.isArray()){f' || {null_check}' if is_optional else ''}"
|
|
622
|
+
elif field_type.type_name.startswith("Map<"):
|
|
623
|
+
values_type = field_type.type_name[4:-1]
|
|
624
|
+
class_definition += f"({node_check}.isObject()){f' || {null_check}' if is_optional else ''}"
|
|
625
|
+
elif field_type.is_class:
|
|
626
|
+
class_definition += f"({null_check} || {field_type.type_name}.isJsonMatch({element_name}))"
|
|
627
|
+
elif field_type.is_enum:
|
|
628
|
+
class_definition += f"({null_check} || ({node_check}.isTextual() && Enum.valueOf({field_type.type_name}.class, {element_name}.asText()) != null))"
|
|
629
|
+
else:
|
|
630
|
+
is_union = False
|
|
631
|
+
field_union = pascal(element_name) + 'Union'
|
|
632
|
+
if field_type == field_union:
|
|
633
|
+
field_union = class_name + "." + pascal(element_name) + 'Union'
|
|
634
|
+
type_kind = self.generated_types_avro_namespace[field_union] if field_union in self.generated_types_avro_namespace else "class"
|
|
635
|
+
if type_kind == "union":
|
|
636
|
+
is_union = True
|
|
637
|
+
class_definition += f"({null_check} || {field_type}.isJsonMatch({element_name}))"
|
|
638
|
+
if not is_union:
|
|
639
|
+
class_definition += f"({node_check}.isObject()){f' || {null_check}' if is_optional else ''}"
|
|
640
|
+
|
|
641
|
+
return class_definition
|
|
642
|
+
|
|
643
|
+
def generate_avro_get_method(self, class_name: str, fields: List[Dict], parent_package: str) -> str:
|
|
644
|
+
""" Generates the get method for SpecificRecord """
|
|
645
|
+
get_method = f"\n{INDENT}@Override\n{INDENT}public Object get(int field$) {{\n"
|
|
646
|
+
get_method += f"{INDENT * 2}switch (field$) {{\n"
|
|
647
|
+
for index, field in enumerate(fields):
|
|
648
|
+
field_name = pascal(field['name']) if self.pascal_properties else field['name']
|
|
649
|
+
field_name = self.safe_identifier(field_name, class_name)
|
|
650
|
+
field_type = self.convert_avro_type_to_java(class_name, field_name, field['type'], parent_package)
|
|
651
|
+
if field_type.type_name in self.generated_types_avro_namespace and self.generated_types_avro_namespace[field_type.type_name] == "union":
|
|
652
|
+
get_method += f"{INDENT * 3}case {index}: return this.{field_name}!=null?this.{field_name}.toObject():null;\n"
|
|
653
|
+
else:
|
|
654
|
+
get_method += f"{INDENT * 3}case {index}: return this.{field_name};\n"
|
|
655
|
+
get_method += f"{INDENT * 3}default: throw new AvroRuntimeException(\"Bad index: \" + field$);\n"
|
|
656
|
+
get_method += f"{INDENT * 2}}}\n{INDENT}}}\n"
|
|
657
|
+
return get_method
|
|
658
|
+
|
|
659
|
+
def generate_avro_put_method(self, class_name: str, fields: List[Dict], parent_package: str) -> str:
|
|
660
|
+
""" Generates the put method for SpecificRecord """
|
|
661
|
+
suppress_unchecked = False
|
|
662
|
+
put_method = f"\n{INDENT}@Override\n{INDENT}public void put(int field$, Object value$) {{\n"
|
|
663
|
+
put_method += f"{INDENT * 2}switch (field$) {{\n"
|
|
664
|
+
for index, field in enumerate(fields):
|
|
665
|
+
field_name = pascal(field['name']) if self.pascal_properties else field['name']
|
|
666
|
+
field_name = self.safe_identifier(field_name, class_name)
|
|
667
|
+
field_type = self.convert_avro_type_to_java(class_name, field_name, field['type'], parent_package)
|
|
668
|
+
if field_type.type_name.startswith("List<") or field_type.type_name.startswith("Map<"):
|
|
669
|
+
suppress_unchecked = True
|
|
670
|
+
if field_type.type_name in self.generated_types_avro_namespace and self.generated_types_avro_namespace[field_type.type_name] == "union":
|
|
671
|
+
put_method += f"{INDENT * 3}case {index}: this.{field_name} = new {field_type.type_name}((GenericData.Record)value$); break;\n"
|
|
672
|
+
else:
|
|
673
|
+
if field_type.type_name == 'String':
|
|
674
|
+
put_method += f"{INDENT * 3}case {index}: this.{field_name} = value$.toString(); break;\n"
|
|
675
|
+
else:
|
|
676
|
+
put_method += f"{INDENT * 3}case {index}: this.{field_name} = ({field_type.type_name})value$; break;\n"
|
|
677
|
+
put_method += f"{INDENT * 3}default: throw new AvroRuntimeException(\"Bad index: \" + field$);\n"
|
|
678
|
+
put_method += f"{INDENT * 2}}}\n{INDENT}}}\n"
|
|
679
|
+
if suppress_unchecked:
|
|
680
|
+
put_method = f"\n{INDENT}@SuppressWarnings(\"unchecked\"){put_method}"
|
|
681
|
+
return put_method
|
|
682
|
+
|
|
683
|
+
def generate_enum(self, avro_schema: Dict, parent_package: str, write_file: bool) -> JavaType:
|
|
684
|
+
""" Generates a Java enum from an Avro enum schema """
|
|
685
|
+
enum_definition = ''
|
|
686
|
+
if 'doc' in avro_schema:
|
|
687
|
+
enum_definition += f"/** {avro_schema['doc']} */\n"
|
|
688
|
+
|
|
689
|
+
package = self.join_packages(self.base_package, avro_schema.get('namespace', parent_package)).replace('.', '/').lower()
|
|
690
|
+
enum_name = self.safe_identifier(avro_schema['name'])
|
|
691
|
+
type_name = self.qualified_name(package.replace('/', '.'), enum_name)
|
|
692
|
+
self.generated_types_avro_namespace[self.qualified_name(avro_schema.get('namespace', parent_package),avro_schema['name'])] = "enum"
|
|
693
|
+
self.generated_types_java_package[type_name] = "enum"
|
|
694
|
+
symbols = avro_schema.get('symbols', [])
|
|
695
|
+
symbols_str = ', '.join([symbol.upper() for symbol in symbols])
|
|
696
|
+
enum_definition += f"public enum {enum_name} {{\n"
|
|
697
|
+
enum_definition += f"{INDENT}{symbols_str};\n"
|
|
698
|
+
enum_definition += "}\n"
|
|
699
|
+
if write_file:
|
|
700
|
+
self.write_to_file(package, enum_name, enum_definition)
|
|
701
|
+
return AvroToJava.JavaType(type_name, is_enum=True)
|
|
702
|
+
|
|
703
|
+
def generate_embedded_union_class_jackson(self, class_name: str, field_name: str, avro_type: List, parent_package: str, write_file: bool) -> str:
|
|
704
|
+
""" Generates an embedded Union Class for Java using Jackson """
|
|
705
|
+
class_definition_ctors = class_definition_decls = class_definition_read = class_definition_write = class_definition = ''
|
|
706
|
+
class_definition_toobject = class_definition_fromobjectctor = class_definition_genericrecordctor = ''
|
|
707
|
+
|
|
708
|
+
list_is_json_match: List[str] = []
|
|
709
|
+
union_class_name = class_name + pascal(field_name) + 'Union'
|
|
710
|
+
package = self.join_packages(self.base_package, parent_package).replace('.', '/').lower()
|
|
711
|
+
union_types: List[AvroToJava.JavaType] = [self.convert_avro_type_to_java(class_name, field_name + "Option" + str(i), t, parent_package) for i, t in enumerate(avro_type)]
|
|
712
|
+
for i, union_type in enumerate(union_types):
|
|
713
|
+
# we need the nullable version (wrapper) of all primitive types
|
|
714
|
+
if self.is_java_primitive(union_type):
|
|
715
|
+
union_type = self.map_primitive_to_java(union_type.type_name, True)
|
|
716
|
+
union_variable_name = union_type.type_name
|
|
717
|
+
is_dict = is_list = False
|
|
718
|
+
if union_type.type_name.startswith("Map<"):
|
|
719
|
+
# handle Map types
|
|
720
|
+
is_dict = True
|
|
721
|
+
# find the comma
|
|
722
|
+
union_variable_name = flatten_type_name(union_type.type_name)
|
|
723
|
+
elif union_type.type_name.startswith("List<"):
|
|
724
|
+
# handle List types
|
|
725
|
+
is_list = True
|
|
726
|
+
union_variable_name = flatten_type_name(union_type.type_name)
|
|
727
|
+
elif union_type.type_name == "byte[]":
|
|
728
|
+
union_variable_name = "Bytes"
|
|
729
|
+
else:
|
|
730
|
+
union_variable_name = union_type.type_name.rsplit('.', 1)[-1]
|
|
731
|
+
|
|
732
|
+
union_variable_name = self.safe_identifier(union_variable_name, class_name)
|
|
733
|
+
|
|
734
|
+
# Constructor for each type
|
|
735
|
+
class_definition_ctors += \
|
|
736
|
+
f"{INDENT*1}public {union_class_name}({union_type.type_name} {union_variable_name}) {{\n{INDENT*2}this._{camel(union_variable_name)} = {union_variable_name};\n{INDENT*1}}}\n"
|
|
737
|
+
|
|
738
|
+
# Declarations
|
|
739
|
+
class_definition_decls += \
|
|
740
|
+
f"{INDENT*1}private {union_type.type_name} _{camel(union_variable_name)};\n" + \
|
|
741
|
+
f"{INDENT*1}public {union_type.type_name} get{union_variable_name}() {{ return _{camel(union_variable_name)}; }}\n";
|
|
742
|
+
|
|
743
|
+
class_definition_toobject += f"{INDENT*2}if (_{camel(union_variable_name)} != null) {{\n{INDENT*3}return _{camel(union_variable_name)};\n{INDENT*2}}}\n"
|
|
744
|
+
|
|
745
|
+
if self.avro_annotation and union_type.is_class:
|
|
746
|
+
class_definition_genericrecordctor += f"{INDENT*2}if ( {union_type.type_name}.AVROSCHEMA.getName().equals(record.getSchema().getName()) && {union_type.type_name}.AVROSCHEMA.getNamespace().equals(record.getSchema().getNamespace()) ) {{"
|
|
747
|
+
class_definition_genericrecordctor += f"\n{INDENT*3}this._{camel(union_variable_name)} = new {union_type.type_name}(record);\n{INDENT*3}return;\n{INDENT*2}}}\n"
|
|
748
|
+
|
|
749
|
+
# there can only be one list and one map in the union, so we don't need to differentiate this any further
|
|
750
|
+
if is_list:
|
|
751
|
+
class_definition_fromobjectctor += f"{INDENT*2}if (obj instanceof List<?>) {{\n{INDENT*3}this._{camel(union_variable_name)} = ({union_type.type_name})obj;\n{INDENT*3}return;\n{INDENT*2}}}\n"
|
|
752
|
+
elif is_dict:
|
|
753
|
+
class_definition_fromobjectctor += f"{INDENT*2}if (obj instanceof Map<?,?>) {{\n{INDENT*3}this._{camel(union_variable_name)} = ({union_type.type_name})obj;\n{INDENT*3}return;\n{INDENT*2}}}\n"
|
|
754
|
+
else:
|
|
755
|
+
class_definition_fromobjectctor += f"{INDENT*2}if (obj instanceof {union_type.type_name}) {{\n{INDENT*3}this._{camel(union_variable_name)} = ({union_type.type_name})obj;\n{INDENT*3}return;\n{INDENT*2}}}\n"
|
|
756
|
+
|
|
757
|
+
# Read method logic
|
|
758
|
+
if is_dict:
|
|
759
|
+
class_definition_read += f"{INDENT*3}if (node.isObject()) {{\n{INDENT*4}{union_type.type_name} map = mapper.readValue(node.toString(), new TypeReference<{union_type.type_name}>(){{}});\n{INDENT*3}return new {union_class_name}(map);\n{INDENT*3}}}\n"
|
|
760
|
+
elif is_list:
|
|
761
|
+
class_definition_read += f"{INDENT*3}if (node.isArray()) {{\n{INDENT*4}{union_type.type_name} list = mapper.readValue(node.toString(), new TypeReference<{union_type.type_name}>(){{}});\n{INDENT*4}return new {union_class_name}(list);\n{INDENT*3}}}\n"
|
|
762
|
+
elif self.is_java_primitive(union_type):
|
|
763
|
+
if union_type.type_name == "String":
|
|
764
|
+
class_definition_read += f"{INDENT*3}if (node.isTextual()) {{\n{INDENT*4}return new {union_class_name}(node.asText());\n{INDENT*3}}}\n"
|
|
765
|
+
elif union_type.type_name == "byte[]":
|
|
766
|
+
class_definition_read += f"{INDENT*3}if (node.isBinary()) {{\n{INDENT*4}return new {union_class_name}(node.binaryValue());\n{INDENT*3}}}\n"
|
|
767
|
+
elif union_type.type_name in ["int", "Int"]:
|
|
768
|
+
class_definition_read += f"{INDENT*3}if (node.canConvertToInt()) {{\n{INDENT*4}return new {union_class_name}(node.asInt());\n{INDENT*3}}}\n"
|
|
769
|
+
elif union_type.type_name in ["long", "Long"]:
|
|
770
|
+
class_definition_read += f"{INDENT*3}if (node.canConvertToLong()) {{\n{INDENT*4}return new {union_class_name}(node.asLong());\n{INDENT*3}}}\n"
|
|
771
|
+
elif union_type.type_name in ["float", "Float"]:
|
|
772
|
+
class_definition_read += f"{INDENT*3}if (node.isFloat()) {{\n{INDENT*4}return new {union_class_name}(node.floatValue());\n{INDENT*3}}}\n"
|
|
773
|
+
elif union_type.type_name in ["double", "Double"]:
|
|
774
|
+
class_definition_read += f"{INDENT*3}if (node.isDouble()) {{\n{INDENT*4}return new {union_class_name}(node.doubleValue());\n{INDENT*3}}}\n"
|
|
775
|
+
elif union_type.type_name == "decimal":
|
|
776
|
+
class_definition_read += f"{INDENT*3}if (node.isBigDecimal()) {{\n{INDENT*4}return new {union_class_name}(node.decimalValue());\n{INDENT*3}}}\n"
|
|
777
|
+
elif union_type.type_name in ["boolean", "Boolean"]:
|
|
778
|
+
class_definition_read += f"{INDENT*3}if (node.isBoolean()) {{\n{INDENT*4}return new {union_class_name}(node.asBoolean());\n{INDENT*3}}}\n"
|
|
779
|
+
else:
|
|
780
|
+
if union_type.is_enum:
|
|
781
|
+
class_definition_read += f"{INDENT*3}if (node.isTextual()) {{\n{INDENT*4}return new {union_class_name}(Enum.valueOf({union_type.type_name}.class, node.asText()));\n{INDENT*3}}}\n"
|
|
782
|
+
else:
|
|
783
|
+
class_definition_read += f"{INDENT*3}if (node.isObject() && {union_type.type_name}.isJsonMatch(node)) {{\n{INDENT*4}return new {union_class_name}(mapper.readValue(node.toString(), {union_type.type_name}.class));\n{INDENT*3}}}\n"
|
|
784
|
+
|
|
785
|
+
# Write method logic
|
|
786
|
+
class_definition_write += f"{INDENT*3}{union_type.type_name} {camel(union_variable_name)}Value = value.get{union_variable_name}();\n{INDENT*3}if ({camel(union_variable_name)}Value != null) {{\n{INDENT*4}generator.writeObject({camel(union_variable_name)}Value);\n{INDENT*4}return;\n{INDENT*3}}}\n"
|
|
787
|
+
|
|
788
|
+
# JSON match method logic
|
|
789
|
+
gij = self.get_is_json_match_clause_type("node", class_name, union_type)
|
|
790
|
+
if gij:
|
|
791
|
+
list_is_json_match.append(gij)
|
|
792
|
+
|
|
793
|
+
class_definition = f"@JsonSerialize(using = {union_class_name}.Serializer.class)\n"
|
|
794
|
+
class_definition += f"@JsonDeserialize(using = {union_class_name}.Deserializer.class)\n"
|
|
795
|
+
class_definition += f"public class {union_class_name} {{\n"
|
|
796
|
+
class_definition += class_definition_decls
|
|
797
|
+
class_definition += f"\n{INDENT}public " + union_class_name + "() {}\n"
|
|
798
|
+
if self.avro_annotation:
|
|
799
|
+
class_definition += f"\n{INDENT}public {union_class_name}(GenericData.Record record) {{\n"
|
|
800
|
+
class_definition += class_definition_genericrecordctor
|
|
801
|
+
class_definition += f"{INDENT*2}throw new UnsupportedOperationException(\"No record type is set in the union\");\n"
|
|
802
|
+
class_definition += f"{INDENT}}}\n"
|
|
803
|
+
class_definition += f"\n{INDENT}public {union_class_name}(Object obj) {{\n"
|
|
804
|
+
class_definition += class_definition_fromobjectctor
|
|
805
|
+
class_definition += f"{INDENT*2}throw new UnsupportedOperationException(\"No record type is set in the union\");\n"
|
|
806
|
+
class_definition += f"{INDENT}}}\n"
|
|
807
|
+
class_definition += class_definition_ctors
|
|
808
|
+
class_definition += f"\n{INDENT}public Object toObject() {{\n"
|
|
809
|
+
class_definition += class_definition_toobject
|
|
810
|
+
class_definition += f"{INDENT*2}throw new UnsupportedOperationException(\"No record type is set in the union\");\n"
|
|
811
|
+
class_definition += f"{INDENT}}}\n"
|
|
812
|
+
class_definition += f"\n{INDENT}public static class Serializer extends JsonSerializer<" + union_class_name + "> {\n"
|
|
813
|
+
class_definition += f"{INDENT*2}@Override\n"
|
|
814
|
+
class_definition += f"{INDENT*2}public void serialize(" + union_class_name + " value, JsonGenerator generator, SerializerProvider serializers) throws IOException {\n"
|
|
815
|
+
class_definition += class_definition_write
|
|
816
|
+
class_definition += f"{INDENT*3}throw new UnsupportedOperationException(\"No record type is set in the union\");\n"
|
|
817
|
+
class_definition += f"{INDENT*2}}}\n{INDENT}}}\n"
|
|
818
|
+
class_definition += f"\n{INDENT}public static class Deserializer extends JsonDeserializer<" + union_class_name + "> {\n"
|
|
819
|
+
class_definition += f"{INDENT*2}@Override\n"
|
|
820
|
+
class_definition += f"{INDENT*2}public " + union_class_name + " deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {\n"
|
|
821
|
+
class_definition += f"{INDENT*3}ObjectMapper mapper = (ObjectMapper) p.getCodec();\n"
|
|
822
|
+
class_definition += f"{INDENT*3}JsonNode node = mapper.readTree(p);\n"
|
|
823
|
+
class_definition += class_definition_read
|
|
824
|
+
class_definition += f"{INDENT*3}throw new UnsupportedOperationException(\"No record type matched the JSON data\");\n"
|
|
825
|
+
class_definition += f"{INDENT*2}}}\n{INDENT}}}\n"
|
|
826
|
+
class_definition += f"\n{INDENT*1}public static boolean isJsonMatch(JsonNode node) {{\n"
|
|
827
|
+
class_definition += f"{INDENT*2}return " + " || ".join(list_is_json_match) + ";\n"
|
|
828
|
+
class_definition += f"{INDENT*1}}}\n}}\n"
|
|
829
|
+
|
|
830
|
+
if write_file:
|
|
831
|
+
self.write_to_file(package, union_class_name, class_definition)
|
|
832
|
+
self.generated_types_avro_namespace[union_class_name] = "union" # Track union types
|
|
833
|
+
self.generated_types_java_package[union_class_name] = "union" # Track union types
|
|
834
|
+
return union_class_name
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
def generate_property(self, class_name: str, field: Dict, parent_package: str) -> str:
|
|
838
|
+
""" Generates a Java property definition """
|
|
839
|
+
field_name = pascal(field['name']) if self.pascal_properties else field['name']
|
|
840
|
+
field_type = self.convert_avro_type_to_java(class_name, field_name, field['type'], parent_package)
|
|
841
|
+
safe_field_name = self.safe_identifier(field_name, class_name)
|
|
842
|
+
property_def = ''
|
|
843
|
+
if 'doc' in field:
|
|
844
|
+
property_def += f"{INDENT}/** {field['doc']} */\n"
|
|
845
|
+
if self.jackson_annotations:
|
|
846
|
+
property_def += f"{INDENT}@JsonProperty(\"{field['name']}\")\n"
|
|
847
|
+
property_def += f"{INDENT}private {field_type.type_name} {safe_field_name};\n"
|
|
848
|
+
property_def += f"{INDENT}public {field_type.type_name} get{pascal(field_name)}() {{ return {safe_field_name}; }}\n"
|
|
849
|
+
property_def += f"{INDENT}public void set{pascal(field_name)}({field_type.type_name} {safe_field_name}) {{ this.{safe_field_name} = {safe_field_name}; }}\n"
|
|
850
|
+
if field_type.union_types:
|
|
851
|
+
for union_type in field_type.union_types:
|
|
852
|
+
if union_type.type_name.startswith("List<") or union_type.type_name.startswith("Map<"):
|
|
853
|
+
property_def += f"{INDENT}@SuppressWarnings(\"unchecked\")\n"
|
|
854
|
+
property_def += f"{INDENT}public {union_type.type_name} get{pascal(field_name)}As{flatten_type_name(union_type.type_name)}() {{ return ({union_type.type_name}){safe_field_name}; }}\n"
|
|
855
|
+
property_def += f"{INDENT}public void set{pascal(field_name)}As{flatten_type_name(union_type.type_name)}({union_type.type_name} {safe_field_name}) {{ this.{safe_field_name} = {safe_field_name}; }}\n"
|
|
856
|
+
return property_def
|
|
857
|
+
|
|
858
|
+
def write_to_file(self, package: str, name: str, definition: str):
|
|
859
|
+
""" Writes a Java class or enum to a file """
|
|
860
|
+
package = package.lower()
|
|
861
|
+
package = self.safe_package(package)
|
|
862
|
+
directory_path = os.path.join(
|
|
863
|
+
self.output_dir, package.replace('.', os.sep).replace('/', os.sep))
|
|
864
|
+
if not os.path.exists(directory_path):
|
|
865
|
+
os.makedirs(directory_path, exist_ok=True)
|
|
866
|
+
file_path = os.path.join(directory_path, f"{name}.java")
|
|
867
|
+
|
|
868
|
+
with open(file_path, 'w', encoding='utf-8') as file:
|
|
869
|
+
if package:
|
|
870
|
+
file.write(f"package {package.replace('/', '.')};\n\n")
|
|
871
|
+
if "List<" in definition:
|
|
872
|
+
file.write("import java.util.List;\n")
|
|
873
|
+
if "Map<" in definition:
|
|
874
|
+
file.write("import java.util.Map;\n")
|
|
875
|
+
if "Predicate<" in definition:
|
|
876
|
+
file.write("import java.util.function.Predicate;\n")
|
|
877
|
+
if "BigDecimal" in definition:
|
|
878
|
+
file.write("import java.math.BigDecimal;\n")
|
|
879
|
+
if "LocalDate" in definition:
|
|
880
|
+
file.write("import java.time.LocalDate;\n")
|
|
881
|
+
if "LocalTime" in definition:
|
|
882
|
+
file.write("import java.time.LocalTime;\n")
|
|
883
|
+
if "Instant" in definition:
|
|
884
|
+
file.write("import java.time.Instant;\n")
|
|
885
|
+
if "LocalDateTime" in definition:
|
|
886
|
+
file.write("import java.time.LocalDateTime;\n")
|
|
887
|
+
if "UUID" in definition:
|
|
888
|
+
file.write("import java.util.UUID;\n")
|
|
889
|
+
if "Duration" in definition:
|
|
890
|
+
file.write("import java.time.Duration;\n")
|
|
891
|
+
|
|
892
|
+
if self.avro_annotation:
|
|
893
|
+
if 'AvroRuntimeException' in definition:
|
|
894
|
+
file.write("import org.apache.avro.AvroRuntimeException;\n")
|
|
895
|
+
if 'Schema' in definition:
|
|
896
|
+
file.write("import org.apache.avro.Schema;\n")
|
|
897
|
+
if 'GenericData' in definition:
|
|
898
|
+
file.write("import org.apache.avro.generic.GenericData;\n")
|
|
899
|
+
if 'DatumReader' in definition:
|
|
900
|
+
file.write("import org.apache.avro.io.DatumReader;\n")
|
|
901
|
+
if 'DatumWriter' in definition:
|
|
902
|
+
file.write("import org.apache.avro.io.DatumWriter;\n")
|
|
903
|
+
if 'DecoderFactory' in definition:
|
|
904
|
+
file.write("import org.apache.avro.io.DecoderFactory;\n")
|
|
905
|
+
if 'EncoderFactory' in definition:
|
|
906
|
+
file.write("import org.apache.avro.io.EncoderFactory;\n")
|
|
907
|
+
if 'SpecificDatumReader' in definition:
|
|
908
|
+
file.write("import org.apache.avro.specific.SpecificDatumReader;\n")
|
|
909
|
+
if 'SpecificDatumWriter' in definition:
|
|
910
|
+
file.write("import org.apache.avro.specific.SpecificDatumWriter;\n")
|
|
911
|
+
if 'SpecificRecord' in definition:
|
|
912
|
+
file.write("import org.apache.avro.specific.SpecificRecord;\n")
|
|
913
|
+
if 'Encoder' in definition:
|
|
914
|
+
file.write("import org.apache.avro.io.Encoder;\n")
|
|
915
|
+
if self.jackson_annotations:
|
|
916
|
+
if 'JsonNode' in definition:
|
|
917
|
+
file.write("import com.fasterxml.jackson.databind.JsonNode;\n")
|
|
918
|
+
if 'ObjectMapper' in definition:
|
|
919
|
+
file.write("import com.fasterxml.jackson.databind.ObjectMapper;\n")
|
|
920
|
+
if 'JsonSerialize' in definition:
|
|
921
|
+
file.write("import com.fasterxml.jackson.databind.annotation.JsonSerialize;\n")
|
|
922
|
+
if 'JsonDeserialize' in definition:
|
|
923
|
+
file.write("import com.fasterxml.jackson.databind.annotation.JsonDeserialize;\n")
|
|
924
|
+
if 'JsonSerializer' in definition:
|
|
925
|
+
file.write("import com.fasterxml.jackson.databind.JsonSerializer;\n")
|
|
926
|
+
if 'SerializerProvider' in definition:
|
|
927
|
+
file.write("import com.fasterxml.jackson.databind.SerializerProvider;\n")
|
|
928
|
+
if 'JsonDeserializer' in definition:
|
|
929
|
+
file.write("import com.fasterxml.jackson.databind.JsonDeserializer;\n")
|
|
930
|
+
if 'DeserializationContext' in definition:
|
|
931
|
+
file.write("import com.fasterxml.jackson.databind.DeserializationContext;\n")
|
|
932
|
+
if 'JsonParser' in definition:
|
|
933
|
+
file.write("import com.fasterxml.jackson.core.JsonParser;\n")
|
|
934
|
+
if 'JsonIgnore' in definition:
|
|
935
|
+
file.write("import com.fasterxml.jackson.annotation.JsonIgnore;\n")
|
|
936
|
+
if 'JsonProperty' in definition:
|
|
937
|
+
file.write("import com.fasterxml.jackson.annotation.JsonProperty;\n")
|
|
938
|
+
if 'JsonProcessingException' in definition:
|
|
939
|
+
file.write("import com.fasterxml.jackson.core.JsonProcessingException;\n")
|
|
940
|
+
if 'JsonGenerator' in definition:
|
|
941
|
+
file.write("import com.fasterxml.jackson.core.JsonGenerator;\n")
|
|
942
|
+
if 'TypeReference' in definition:
|
|
943
|
+
file.write("import com.fasterxml.jackson.core.type.TypeReference;\n")
|
|
944
|
+
if self.avro_annotation or self.jackson_annotations:
|
|
945
|
+
if 'GZIPOutputStream' in definition:
|
|
946
|
+
file.write("import java.util.zip.GZIPOutputStream;\n")
|
|
947
|
+
if 'GZIPInputStream' in definition:
|
|
948
|
+
file.write("import java.util.zip.GZIPInputStream;\n")
|
|
949
|
+
if 'ByteArrayInputStream' in definition:
|
|
950
|
+
file.write("import java.io.ByteArrayInputStream;\n")
|
|
951
|
+
if "ByteArrayOutputStream" in definition:
|
|
952
|
+
file.write("import java.io.ByteArrayOutputStream;\n")
|
|
953
|
+
if "InputStream" in definition:
|
|
954
|
+
file.write("import java.io.InputStream;\n")
|
|
955
|
+
if "IOException" in definition:
|
|
956
|
+
file.write("import java.io.IOException;\n")
|
|
957
|
+
if "InflaterInputStream" in definition:
|
|
958
|
+
file.write("import java.util.zip.InflaterInputStream;\n")
|
|
959
|
+
file.write("\n")
|
|
960
|
+
file.write(definition)
|
|
961
|
+
|
|
962
|
+
def convert_schema(self, schema: JsonNode, output_dir: str):
|
|
963
|
+
"""Converts Avro schema to Java"""
|
|
964
|
+
if not isinstance(schema, list):
|
|
965
|
+
schema = [schema]
|
|
966
|
+
if not os.path.exists(output_dir):
|
|
967
|
+
os.makedirs(output_dir, exist_ok=True)
|
|
968
|
+
pom_path = os.path.join(output_dir, "pom.xml")
|
|
969
|
+
if not os.path.exists(pom_path):
|
|
970
|
+
package_elements = self.base_package.split('.') if self.base_package else ["com", "example"]
|
|
971
|
+
groupid = '.'.join(package_elements[:-1]) if len(package_elements) > 1 else package_elements[0]
|
|
972
|
+
artifactid = package_elements[-1]
|
|
973
|
+
with open(pom_path, 'w', encoding='utf-8') as file:
|
|
974
|
+
file.write(POM_CONTENT.format(groupid=groupid, artifactid=artifactid, AVRO_VERSION=AVRO_VERSION, JACKSON_VERSION=JACKSON_VERSION, JDK_VERSION=JDK_VERSION, PACKAGE=self.base_package))
|
|
975
|
+
output_dir = os.path.join(
|
|
976
|
+
output_dir, "src/main/java".replace('/', os.sep))
|
|
977
|
+
if not os.path.exists(output_dir):
|
|
978
|
+
os.makedirs(output_dir, exist_ok=True)
|
|
979
|
+
self.output_dir = output_dir
|
|
980
|
+
for avro_schema in (x for x in schema if isinstance(x, dict)):
|
|
981
|
+
self.generate_class_or_enum(avro_schema, '')
|
|
982
|
+
|
|
983
|
+
def convert(self, avro_schema_path: str, output_dir: str):
|
|
984
|
+
"""Converts Avro schema to Java"""
|
|
985
|
+
with open(avro_schema_path, 'r', encoding='utf-8') as file:
|
|
986
|
+
schema = json.load(file)
|
|
987
|
+
self.convert_schema(schema, output_dir)
|
|
988
|
+
|
|
989
|
+
|
|
990
|
+
def convert_avro_to_java(avro_schema_path, java_file_path, package_name='', pascal_properties=False, jackson_annotation=False, avro_annotation=False):
|
|
991
|
+
"""_summary_
|
|
992
|
+
|
|
993
|
+
Converts Avro schema to C# classes
|
|
994
|
+
|
|
995
|
+
Args:
|
|
996
|
+
avro_schema_path (_type_): Avro input schema path
|
|
997
|
+
cs_file_path (_type_): Output C# file path
|
|
998
|
+
"""
|
|
999
|
+
if not package_name:
|
|
1000
|
+
package_name = os.path.splitext(os.path.basename(java_file_path))[0].replace('-', '_').lower()
|
|
1001
|
+
avrotojava = AvroToJava()
|
|
1002
|
+
avrotojava.base_package = package_name
|
|
1003
|
+
avrotojava.pascal_properties = pascal_properties
|
|
1004
|
+
avrotojava.avro_annotation = avro_annotation
|
|
1005
|
+
avrotojava.jackson_annotations = jackson_annotation
|
|
1006
|
+
avrotojava.convert(avro_schema_path, java_file_path)
|
|
1007
|
+
|
|
1008
|
+
|
|
1009
|
+
def convert_avro_schema_to_java(avro_schema: JsonNode, output_dir: str, package_name='', pascal_properties=False, jackson_annotation=False, avro_annotation=False):
|
|
1010
|
+
"""_summary_
|
|
1011
|
+
|
|
1012
|
+
Converts Avro schema to C# classes
|
|
1013
|
+
|
|
1014
|
+
Args:
|
|
1015
|
+
avro_schema (_type_): Avro schema as a dictionary or list of dictionaries
|
|
1016
|
+
output_dir (_type_): Output directory path
|
|
1017
|
+
"""
|
|
1018
|
+
avrotojava = AvroToJava()
|
|
1019
|
+
avrotojava.base_package = package_name
|
|
1020
|
+
avrotojava.pascal_properties = pascal_properties
|
|
1021
|
+
avrotojava.avro_annotation = avro_annotation
|
|
1022
|
+
avrotojava.jackson_annotations = jackson_annotation
|
|
1023
|
+
avrotojava.convert_schema(avro_schema, output_dir)
|