@@ -158,6 +158,7 @@ def generate_class(self, avro_schema: Dict, parent_namespace: str, write_file: b
158
158
'is_primitive' : field ['definition' ]['is_primitive' ],
159
159
'is_enum' : field ['definition' ]['is_enum' ],
160
160
'is_array' : field ['definition' ]['is_array' ],
161
+ 'is_union' : field ['definition' ]['is_union' ],
161
162
'docstring' : field ['docstring' ],
162
163
} for field in fields ]
163
164
@@ -231,12 +232,14 @@ def generate_field(self, field: Dict, parent_namespace: str, import_types: Set[s
231
232
field ['type' ], parent_namespace , import_types_this , class_name , field ['name' ])
232
233
import_types .update (import_types_this )
233
234
field_name = field ['name' ]
235
+ import_name = import_types_this .pop () if len (import_types_this ) > 0 else ''
234
236
return {
235
237
'name' : field_name ,
236
238
'type' : field_type ,
237
239
'is_primitive' : self .is_typescript_primitive (field_type .replace ('[]' , '' )),
238
240
'is_array' : field_type .endswith ('[]' ),
239
- 'is_enum' : len (import_types_this ) > 0 and self .is_enum_type (import_types_this .pop (),'' )
241
+ 'is_union' : self .generated_types .get (import_name , '' ) == 'union' ,
242
+ 'is_enum' : self .generated_types .get (import_name , '' ) == 'enum' ,
240
243
}
241
244
242
245
def get_is_json_match_clause (self , field_name : str , field_type : str , field_is_enum : bool ) -> str :
@@ -281,16 +284,9 @@ def get_is_json_match_clause(self, field_name: str, field_type: str, field_is_en
281
284
def generate_embedded_union (self , class_name : str , field_name : str , avro_type : List , parent_namespace : str , parent_import_types : Set [str ], write_file : bool = True ) -> str :
282
285
"""Generate embedded Union class for a field with namespace support."""
283
286
union_class_name = pascal (field_name ) + 'Union' if field_name else pascal (class_name ) + 'Union'
284
- namespace = parent_namespace
285
- union_types = [self .convert_avro_type_to_typescript ( t , parent_namespace , parent_import_types ) for t in avro_type if t != 'null' ]
286
- import_types = []
287
- for t in avro_type :
288
- if isinstance (t , str ) and t in self .generated_types :
289
- import_types .append (t )
290
- elif isinstance (t , dict ) and 'type' in t and t ['type' ] == "array" and isinstance (t ['items' ], str ) and t ['items' ] in self .generated_types :
291
- import_types .append (t ['items' ])
292
- elif isinstance (t , dict ) and 'type' in t and t ['type' ] == "map" and isinstance (t ['values' ], str ) and t ['values' ] in self .generated_types :
293
- import_types .append (t ['values' ])
287
+ namespace = self .concat_namespace (self .base_package , parent_namespace )
288
+ import_types :Set [str ] = set ()
289
+ union_types = [self .convert_avro_type_to_typescript ( t , parent_namespace , import_types ) for t in avro_type if t != 'null' ]
294
290
if not import_types :
295
291
return '|' .join (union_types )
296
292
class_definition = ''
@@ -299,67 +295,91 @@ def generate_embedded_union(self, class_name: str, field_name: str, avro_type: L
299
295
continue # Avoid importing itself
300
296
import_type_parts = import_type .split ('.' )
301
297
import_type_name = pascal (import_type_parts [- 1 ])
302
- import_namespace = '.' .join (import_type_parts [:- 1 ])
303
298
import_path = '/' .join (import_type_parts )
304
299
current_path = '/' .join (namespace .split ('.' ))
305
300
relative_import_path = os .path .relpath (import_path , current_path ).replace (os .sep , '/' )
306
301
if not relative_import_path .startswith ('.' ):
307
302
relative_import_path = f'./{ relative_import_path } '
308
303
class_definition += f"import {{ { import_type_name } }} from '{ relative_import_path } .js';\n "
304
+
305
+ if self .typed_json_annotation :
306
+ class_definition += "import 'reflect-metadata';\n "
307
+ class_definition += "import { CustomDeserializerParams, CustomSerializerParams } from 'typedjson/lib/types/metadata.js';\n "
308
+
309
309
310
- class_definition += f"\n export namespace { namespace } {{\n "
311
- class_definition += f"{ self .INDENT } export class { union_class_name } {{\n "
310
+ class_definition += f"\n export class { union_class_name } {{\n "
312
311
313
- class_definition += f"{ self .INDENT * 2 } private value: any;\n \n "
312
+ class_definition += f"{ self .INDENT } private value: any;\n \n "
314
313
315
314
# Constructor
316
- class_definition += f"{ self .INDENT * 2 } constructor(value: { ' | ' .join (union_types ) } ) {{\n "
317
- class_definition += f"{ self .INDENT * 3 } this.value = value;\n "
318
- class_definition += f"{ self .INDENT * 2 } }}\n \n "
315
+ class_definition += f"{ self .INDENT } constructor(value: { ' | ' .join (union_types ) } ) {{\n "
316
+ class_definition += f"{ self .INDENT * 2 } this.value = value;\n "
317
+ class_definition += f"{ self .INDENT } }}\n \n "
319
318
320
319
# Method to check which type is set
321
320
for union_type in union_types :
322
- type_check_method = f"{ self .INDENT * 2 } public is{ pascal (union_type )} (): boolean {{\n "
321
+ type_check_method = f"{ self .INDENT } public is{ pascal (union_type )} (): boolean {{\n "
323
322
if union_type .strip () in ['string' , 'number' , 'boolean' ]:
324
- type_check_method += f"{ self .INDENT * 3 } return typeof this.value === '{ union_type .strip ()} ';\n "
323
+ type_check_method += f"{ self .INDENT * 2 } return typeof this.value === '{ union_type .strip ()} ';\n "
325
324
elif union_type .strip () == 'Date' :
326
- type_check_method += f"{ self .INDENT * 3 } return this.value instanceof Date;\n "
325
+ type_check_method += f"{ self .INDENT * 2 } return this.value instanceof Date;\n "
327
326
else :
328
- type_check_method += f"{ self .INDENT * 3 } return this.value instanceof { union_type .strip ()} ;\n "
329
- type_check_method += f"{ self .INDENT * 2 } }}\n \n "
327
+ type_check_method += f"{ self .INDENT * 2 } return this.value instanceof { union_type .strip ()} ;\n "
328
+ type_check_method += f"{ self .INDENT } }}\n \n "
330
329
class_definition += type_check_method
331
330
332
331
# Method to return the current value
333
- class_definition += f"{ self .INDENT * 2 } public toObject(): any {{\n "
334
- class_definition += f"{ self .INDENT * 3 } return this.value;\n "
335
- class_definition += f"{ self .INDENT * 2 } }}\n \n "
332
+ class_definition += f"{ self .INDENT } public toJSON(): string {{\n "
333
+ class_definition += f"{ self .INDENT * 2 } let rawJson : Uint8Array = this.value.toByteArray('application/json');\n "
334
+ class_definition += f"{ self .INDENT * 2 } return new TextDecoder().decode(rawJson);\n "
335
+ class_definition += f"{ self .INDENT } }}\n \n "
336
336
337
337
# Method to check if JSON matches any of the union types
338
- class_definition += f"{ self .INDENT * 2 } public static isJsonMatch(element: any): boolean {{\n "
338
+ class_definition += f"{ self .INDENT } public static isJsonMatch(element: any): boolean {{\n "
339
339
match_clauses = []
340
340
for union_type in union_types :
341
341
match_clauses .append (f"({ self .get_is_json_match_clause ('value' , union_type , False )} )" )
342
- class_definition += f"{ self .INDENT * 3 } return { ' || ' .join (match_clauses )} ;\n "
343
- class_definition += f"{ self .INDENT * 2 } }}\n \n "
342
+ class_definition += f"{ self .INDENT * 2 } return { ' || ' .join (match_clauses )} ;\n "
343
+ class_definition += f"{ self .INDENT } }}\n \n "
344
344
345
345
# Method to deserialize from JSON
346
- class_definition += f"{ self .INDENT * 2 } public static fromData(element: any, contentTypeString: string): { union_class_name } {{\n "
347
- class_definition += f"{ self .INDENT * 3 } const unionTypes = [{ ', ' .join ([t .strip () for t in union_types if not self .is_typescript_primitive (t .strip ())])} ];\n "
348
- class_definition += f"{ self .INDENT * 3 } for (const type of unionTypes) {{\n "
349
- class_definition += f"{ self .INDENT * 4 } if (type.isJsonMatch(element)) {{\n "
350
- class_definition += f"{ self .INDENT * 5 } return new { union_class_name } (type.fromData(element, contentTypeString));\n "
351
- class_definition += f"{ self .INDENT * 4 } }}\n "
346
+ class_definition += f"{ self .INDENT } public static fromData(element: any, contentTypeString: string): { union_class_name } {{\n "
347
+ class_definition += f"{ self .INDENT * 2 } const unionTypes = [{ ', ' .join ([t .strip () for t in union_types if not self .is_typescript_primitive (t .strip ())])} ];\n "
348
+ class_definition += f"{ self .INDENT * 2 } for (const type of unionTypes) {{\n "
349
+ class_definition += f"{ self .INDENT * 3 } if (type.isJsonMatch(element)) {{\n "
350
+ class_definition += f"{ self .INDENT * 4 } return new { union_class_name } (type.fromData(element, contentTypeString));\n "
352
351
class_definition += f"{ self .INDENT * 3 } }}\n "
353
- class_definition += f"{ self .INDENT * 3 } throw new Error('No matching type for union');\n "
354
352
class_definition += f"{ self .INDENT * 2 } }}\n "
355
-
353
+ class_definition += f" { self . INDENT * 2 } throw new Error('No matching type for union'); \n "
356
354
class_definition += f"{ self .INDENT } }}\n "
355
+
356
+ # Method to deserialize from JSON with custom deserializer params
357
+ class_definition += f"{ self .INDENT } public static fromJSON(json: any, params: CustomDeserializerParams): { union_class_name } {{\n "
358
+ class_definition += f"{ self .INDENT * 2 } try {{\n "
359
+ class_definition += f"{ self .INDENT * 3 } return { union_class_name } .fromData(json, 'application/json');\n "
360
+ class_definition += f"{ self .INDENT * 2 } }} catch (error) {{\n "
361
+ class_definition += f"{ self .INDENT * 3 } return params.fallback(json, { union_class_name } );\n "
362
+ class_definition += f"{ self .INDENT * 2 } }}\n "
363
+ class_definition += f"{ self .INDENT } }}\n \n "
364
+
365
+ # Method to serialize to JSON with custom serializer params
366
+ class_definition += f"{ self .INDENT } public static toJSON(obj: any, params: CustomSerializerParams): any {{\n "
367
+ class_definition += f"{ self .INDENT * 2 } try {{\n "
368
+ class_definition += f"{ self .INDENT * 3 } const val = new { union_class_name } (obj);\n "
369
+ class_definition += f"{ self .INDENT * 3 } return val.toJSON();\n "
370
+ class_definition += f"{ self .INDENT * 2 } }} catch (error) {{\n "
371
+ class_definition += f"{ self .INDENT * 3 } return params.fallback(this, { union_class_name } );\n "
372
+ class_definition += f"{ self .INDENT * 2 } }}\n "
373
+ class_definition += f"{ self .INDENT } }}\n \n "
374
+
357
375
class_definition += "}\n "
358
376
359
377
if write_file :
360
378
self .write_to_file (namespace , union_class_name , class_definition )
361
379
362
- return f"{ namespace } .{ union_class_name } "
380
+ parent_import_types .add (f"{ namespace } .{ union_class_name } " )
381
+ self .generated_types [f"{ namespace } .{ union_class_name } " ] = 'union'
382
+ return f"{ union_class_name } "
363
383
364
384
def write_to_file (self , namespace : str , name : str , content : str ):
365
385
"""Write TypeScript class to file in the correct namespace directory."""
0 commit comments