diff --git a/avrotize/avrotize.py b/avrotize/avrotize.py
index 1921d55..22e75d5 100644
--- a/avrotize/avrotize.py
+++ b/avrotize/avrotize.py
@@ -37,9 +37,18 @@ def create_subparsers(subparsers, commands):
if arg['type'] == 'bool':
kwargs['action'] = 'store_true'
del kwargs['type']
- carg = cmd_parser.add_argument(arg['name'], **kwargs)
+ argname = arg['name']
+ if '_' in argname:
+ argname2 = argname.replace('_', '-')
+ carg = cmd_parser.add_argument(argname, argname2, **kwargs)
+ else:
+ carg = cmd_parser.add_argument(arg['name'], **kwargs)
else:
- carg = cmd_parser.add_argument(arg['name'], **kwargs)
+ if '_' in arg['name']:
+ argname2 = arg['name'].replace('_', '-')
+ carg = cmd_parser.add_argument(arg['name'], argname2, **kwargs)
+ else:
+ carg = cmd_parser.add_argument(arg['name'], **kwargs)
carg.required = arg.get('required', True)
def dynamic_import(module, func):
diff --git a/avrotize/avrotocsharp.py b/avrotize/avrotocsharp.py
index ed0fe8d..6cf5ae3 100644
--- a/avrotize/avrotocsharp.py
+++ b/avrotize/avrotocsharp.py
@@ -8,7 +8,7 @@
from typing import Any, Dict, List, Tuple, Union, cast
import uuid
-from avrotize.common import is_generic_avro_type, pascal, process_template
+from avrotize.common import build_flat_type_dict, inline_avro_references, is_generic_avro_type, pascal, process_template
import glob
JsonNode = Dict[str, 'JsonNode'] | List['JsonNode'] | str | None
@@ -38,9 +38,11 @@ def __init__(self, base_namespace: str = '') -> None:
self.pascal_properties = False
self.system_text_json_annotation = False
self.newtonsoft_json_annotation = False
+ self.system_xml_annotation = False
self.avro_annotation = False
self.generated_types: Dict[str,str] = {}
self.generated_avro_types: Dict[str, Dict[str, Union[str, Dict, List]]] = {}
+ self.type_dict = {}
def get_qualified_name(self, namespace: str, name: str) -> str:
""" Concatenates namespace and name with a dot separator """
@@ -67,7 +69,12 @@ def map_primitive_to_csharp(self, avro_type: str) -> str:
'bytes': 'byte[]',
'string': 'string',
}
- return mapping.get(avro_type, 'object')
+ qualified_class_name = 'global::'+self.get_qualified_name(pascal(self.base_namespace), pascal(avro_type))
+ if qualified_class_name in self.generated_avro_types:
+ result = qualified_class_name
+ else:
+ result = mapping.get(avro_type, 'object')
+ return result
def is_csharp_reserved_word(self, word: str) -> bool:
""" Checks if a word is a reserved C# keyword """
@@ -144,6 +151,7 @@ def generate_class(self, avro_schema: Dict, parent_namespace: str, write_file: b
avro_namespace = avro_schema.get('namespace', parent_namespace)
if not 'namespace' in avro_schema:
avro_schema['namespace'] = parent_namespace
+ xml_namespace = avro_schema.get('xmlns', None)
namespace = pascal(self.concat_namespace(self.base_namespace, avro_namespace))
class_name = pascal(avro_schema['name'])
ref = 'global::'+self.get_qualified_name(namespace, class_name)
@@ -151,6 +159,14 @@ def generate_class(self, avro_schema: Dict, parent_namespace: str, write_file: b
return ref
class_definition += f"/// \n/// { avro_schema.get('doc', class_name ) }\n/// \n"
+
+ # Add XML serialization attribute for the class if enabled
+ if self.system_xml_annotation:
+ if xml_namespace:
+ class_definition += f"[XmlRoot(\"{class_name}\", Namespace=\"{xml_namespace}\")]\n"
+ else:
+ class_definition += f"[XmlRoot(\"{class_name}\")]\n"
+
fields_str = [self.generate_property(field, class_name, avro_namespace) for field in avro_schema.get('fields', [])]
class_body = "\n".join(fields_str)
class_definition += f"public partial class {class_name}"
@@ -166,7 +182,9 @@ def generate_class(self, avro_schema: Dict, parent_namespace: str, write_file: b
class_definition += f"{INDENT*2}for (int i = 0; obj.Schema.Fields.Count > i; ++i)\n{INDENT*2}{{\n"
class_definition += f"{INDENT*3}self.Put(i, obj.GetValue(i));\n{INDENT*2}}}\n{INDENT}}}\n"
if self.avro_annotation:
- avro_schema_json = json.dumps(avro_schema)
+
+ local_avro_schema = inline_avro_references(avro_schema.copy(), self.type_dict, '')
+ avro_schema_json = json.dumps(local_avro_schema)
# wrap schema at 80 characters
avro_schema_json = avro_schema_json.replace('"', 'ยง')
avro_schema_json = f"\"+\n{INDENT}\"".join(
@@ -224,7 +242,9 @@ def generate_class(self, avro_schema: Dict, parent_namespace: str, write_file: b
avro_annotation=self.avro_annotation,
system_text_json_annotation=self.system_text_json_annotation,
newtonsoft_json_annotation=self.newtonsoft_json_annotation,
- json_match_clauses=self.create_is_json_match_clauses(avro_schema, avro_namespace, class_name))
+ system_xml_annotation=self.system_xml_annotation,
+ json_match_clauses=self.create_is_json_match_clauses(avro_schema, avro_namespace, class_name)
+ )
class_definition += "\n"+"}"
@@ -303,7 +323,7 @@ def get_is_json_match_clause_type(self, element_name, class_name, field_type) ->
if type_kind == "class":
class_definition += f"({f'{element_name}.ValueKind == System.Text.Json.JsonValueKind.Null || ' if is_optional else ''}{field_type}.IsJsonMatch({element_name}))"
elif type_kind == "enum":
- class_definition += f"({f'{element_name}.ValueKind == System.Text.Json.JsonValueKind.Null ||' if is_optional else ''}({element_name}.ValueKind == System.Text.Json.JsonValueKind.String && Enum.TryParse<{field_type}>({element_name}.GetString(), true, out _ ))))"
+ class_definition += f"({f'{element_name}.ValueKind == System.Text.Json.JsonValueKind.Null ||' if is_optional else ''}({element_name}.ValueKind == System.Text.Json.JsonValueKind.String && Enum.TryParse<{field_type}>({element_name}.GetString(), true, out _ )))"
else:
is_union = False
field_union = pascal(element_name)+'Union'
@@ -322,6 +342,7 @@ def generate_enum(self, avro_schema: Dict, parent_namespace: str, write_file: bo
enum_definition = ''
namespace = pascal(self.concat_namespace(
self.base_namespace, avro_schema.get('namespace', parent_namespace)))
+ xml_namespace = avro_schema.get('xmlns', None)
enum_name = pascal(avro_schema['name'])
ref = 'global::'+self.get_qualified_name(namespace, enum_name)
if ref in self.generated_types:
@@ -329,8 +350,18 @@ def generate_enum(self, avro_schema: Dict, parent_namespace: str, write_file: bo
enum_definition += "#pragma warning disable 1591\n\n"
enum_definition += f"/// \n/// {avro_schema.get('doc', enum_name )}\n/// \n"
- symbols_str = [
- f"{INDENT}{symbol}" for symbol in avro_schema['symbols']]
+
+ # Add XML serialization attribute for the enum if enabled
+ if self.system_xml_annotation:
+ if xml_namespace:
+ enum_definition += f"[XmlType(\"{enum_name}\", Namespace=\"{xml_namespace}\")]\n"
+ else:
+ enum_definition += f"[XmlType(\"{enum_name}\")]\n"
+
+ if self.system_xml_annotation:
+ symbols_str = [f"{INDENT}[XmlEnum(Name=\"{symbol}\")]\n{INDENT}{symbol}" for symbol in avro_schema['symbols']]
+ else:
+ symbols_str = [f"{INDENT}{symbol}" for symbol in avro_schema['symbols']]
enum_body = ",\n".join(symbols_str)
enum_definition += f"public enum {enum_name}\n{{\n{enum_body}\n}}"
@@ -379,43 +410,52 @@ def generate_embedded_union(self, class_name: str, field_name: str, avro_type: L
f"{INDENT*2}public {union_class_name}({union_type}? {union_type_name})\n{INDENT*2}{{\n{INDENT*3}this.{union_type_name} = {union_type_name};\n{INDENT*2}}}\n"
class_definition_decls += \
f"{INDENT*2}/// \n{INDENT*2}/// Gets the {union_type_name} value\n{INDENT*2}/// \n" + \
- f"{INDENT*2}public {union_type}? {union_type_name} {{ get; private set; }} = null;\n"
+ f"{INDENT*2}public {union_type}? {union_type_name} {{ get; set; }} = null;\n"
class_definition_toobject += f"{INDENT*3}if ({union_type_name} != null) {{\n{INDENT*4}return {union_type_name};\n{INDENT*3}}}\n"
- if is_dict:
- class_definition_read += f"{INDENT*3}if (element.ValueKind == JsonValueKind.Object)\n{INDENT*3}{{\n" + \
- f"{INDENT*4}var map = System.Text.Json.JsonSerializer.Deserialize<{union_type}>(element, options);\n" + \
- f"{INDENT*4}if (map != null) {{ return new {union_class_name}(map); }} else {{ throw new NotSupportedException(); }};\n" + \
- f"{INDENT*3}}}\n"
- elif is_list:
- class_definition_read += f"{INDENT*3}if (element.ValueKind == JsonValueKind.Array)\n{INDENT*3}{{\n" + \
- f"{INDENT*4}var map = System.Text.Json.JsonSerializer.Deserialize<{union_type}>(element, options);\n" + \
- f"{INDENT*4}if (map != null) {{ return new {union_class_name}(map); }} else {{ throw new NotSupportedException(); }};\n" + \
- f"{INDENT*3}}}\n"
- elif self.is_csharp_primitive_type(union_type):
- if union_type == "byte[]":
- class_definition_read += f"{INDENT*3}if (element.ValueKind == JsonValueKind.String)\n{INDENT*3}{{\n{INDENT*4}return new {union_class_name}(element.GetBytesFromBase64());\n{INDENT*3}}}\n"
- if union_type == "string":
- class_definition_read += f"{INDENT*3}if (element.ValueKind == JsonValueKind.String)\n{INDENT*3}{{\n{INDENT*4}return new {union_class_name}(element.GetString());\n{INDENT*3}}}\n"
- elif union_type in ['int', 'long', 'float', 'double', 'decimal', 'short', 'sbyte', 'ushort', 'uint', 'ulong']:
- class_definition_read += f"{INDENT*3}if (element.ValueKind == JsonValueKind.Number)\n{INDENT*3}{{\n{INDENT*4}return new {union_class_name}(element.Get{self.map_csharp_primitive_to_clr_type(union_type)}());\n{INDENT*3}}}\n"
- elif union_type == "bool":
- class_definition_read += f"{INDENT*3}if (element.ValueKind == JsonValueKind.True || element.ValueKind == JsonValueKind.False)\n{INDENT*2}{{\n{INDENT*3}return new {union_class_name}(element.GetBoolean());\n{INDENT*3}}}\n"
- elif union_type == "DateTime":
- class_definition_read += f"{INDENT*3}if (element.ValueKind == JsonValueKind.String)\n{INDENT*3}{{\n{INDENT*4}return new {union_class_name}(System.DateTime.Parse(element.GetString()));\n{INDENT*3}}}\n"
- elif union_type == "DateTimeOffset":
- class_definition_read += f"{INDENT*3}if (element.ValueKind == JsonValueKind.String)\n{INDENT*3}{{\n{INDENT*4}return new {union_class_name}(System.DateTimeOffset.Parse(element.GetString()));\n{INDENT*3}}}\n"
- else:
- class_definition_read += f"{INDENT*3}if ({union_type}.IsJsonMatch(element))\n{INDENT*3}{{\n{INDENT*4}return new {union_class_name}({union_type}.FromData(element, System.Net.Mime.MediaTypeNames.Application.Json));\n{INDENT*3}}}\n"
- class_definition_write += f"{INDENT*3}{'else ' if i>0 else ''}if (value.{union_type_name} != null)\n{INDENT*3}{{\n{INDENT*4}System.Text.Json.JsonSerializer.Serialize(writer, value.{union_type_name}, options);\n{INDENT*3}}}\n"
- gij = self.get_is_json_match_clause_type("element", class_name, union_type)
- if gij:
- list_is_json_match.append(gij)
+ if self.system_text_json_annotation:
+ if is_dict:
+ class_definition_read += f"{INDENT*3}if (element.ValueKind == JsonValueKind.Object)\n{INDENT*3}{{\n" + \
+ f"{INDENT*4}var map = System.Text.Json.JsonSerializer.Deserialize<{union_type}>(element, options);\n" + \
+ f"{INDENT*4}if (map != null) {{ return new {union_class_name}(map); }} else {{ throw new NotSupportedException(); }};\n" + \
+ f"{INDENT*3}}}\n"
+ elif is_list:
+ class_definition_read += f"{INDENT*3}if (element.ValueKind == JsonValueKind.Array)\n{INDENT*3}{{\n" + \
+ f"{INDENT*4}var map = System.Text.Json.JsonSerializer.Deserialize<{union_type}>(element, options);\n" + \
+ f"{INDENT*4}if (map != null) {{ return new {union_class_name}(map); }} else {{ throw new NotSupportedException(); }};\n" + \
+ f"{INDENT*3}}}\n"
+ elif self.is_csharp_primitive_type(union_type):
+ if union_type == "byte[]":
+ class_definition_read += f"{INDENT*3}if (element.ValueKind == JsonValueKind.String)\n{INDENT*3}{{\n{INDENT*4}return new {union_class_name}(element.GetBytesFromBase64());\n{INDENT*3}}}\n"
+ if union_type == "string":
+ class_definition_read += f"{INDENT*3}if (element.ValueKind == JsonValueKind.String)\n{INDENT*3}{{\n{INDENT*4}return new {union_class_name}(element.GetString());\n{INDENT*3}}}\n"
+ elif union_type in ['int', 'long', 'float', 'double', 'decimal', 'short', 'sbyte', 'ushort', 'uint', 'ulong']:
+ class_definition_read += f"{INDENT*3}if (element.ValueKind == JsonValueKind.Number)\n{INDENT*3}{{\n{INDENT*4}return new {union_class_name}(element.Get{self.map_csharp_primitive_to_clr_type(union_type)}());\n{INDENT*3}}}\n"
+ elif union_type == "bool":
+ class_definition_read += f"{INDENT*3}if (element.ValueKind == JsonValueKind.True || element.ValueKind == System.Text.Json.JsonValueKind.False)\n{INDENT*2}{{\n{INDENT*3}return new {union_class_name}(element.GetBoolean());\n{INDENT*3}}}\n"
+ elif union_type == "DateTime":
+ class_definition_read += f"{INDENT*3}if (element.ValueKind == JsonValueKind.String)\n{INDENT*3}{{\n{INDENT*4}return new {union_class_name}(System.DateTime.Parse(element.GetString()));\n{INDENT*3}}}\n"
+ elif union_type == "DateTimeOffset":
+ class_definition_read += f"{INDENT*3}if (element.ValueKind == JsonValueKind.String)\n{INDENT*3}{{\n{INDENT*4}return new {union_class_name}(System.DateTimeOffset.Parse(element.GetString()));\n{INDENT*3}}}\n"
+ else:
+ if union_type.startswith("global::"):
+ type_kind = self.generated_types[union_type] if union_type in self.generated_types else "class"
+ if type_kind == "class":
+ class_definition_read += f"{INDENT*3}if ({union_type}.IsJsonMatch(element))\n{INDENT*3}{{\n{INDENT*4}return new {union_class_name}({union_type}.FromData(element, System.Net.Mime.MediaTypeNames.Application.Json));\n{INDENT*3}}}\n"
+ elif type_kind == "enum":
+ class_definition_read += f"{INDENT*3}if (element.ValueKind == JsonValueKind.String && Enum.TryParse<{union_type}>(element.GetString(), true, out _ ))\n{INDENT*3}{{\n{INDENT*4}return new {union_class_name}(Enum.Parse<{union_type}>(element.GetString()));\n{INDENT*3}}}\n"
+ class_definition_write += f"{INDENT*3}{'else ' if i>0 else ''}if (value.{union_type_name} != null)\n{INDENT*3}{{\n{INDENT*4}System.Text.Json.JsonSerializer.Serialize(writer, value.{union_type_name}, options);\n{INDENT*3}}}\n"
+ gij = self.get_is_json_match_clause_type("element", class_name, union_type)
+ if gij:
+ list_is_json_match.append(gij)
class_definition = \
f"/// \n/// {class_name}. Type union resolver. \n/// \n" + \
f"public partial class {class_name}\n{{\n" + \
f"{INDENT}/// \n{INDENT}/// Union class for {field_name}\n{INDENT}/// \n"
+ if self.system_xml_annotation:
+ class_definition += \
+ f"{INDENT}[XmlRoot(\"{union_class_name}\")]\n"
if self.system_text_json_annotation:
class_definition += \
f"{INDENT}[System.Text.Json.Serialization.JsonConverter(typeof({union_class_name}))]\n"
@@ -519,6 +559,15 @@ def generate_property(self, field: Dict, class_name: str, parent_namespace: str)
field_name += "_"
prop = ''
prop += f"{INDENT}/// \n{INDENT}/// { field.get('doc', field_name) }\n{INDENT}/// \n"
+
+ # Add XML serialization attribute if enabled
+ if self.system_xml_annotation:
+ xmlkind = field.get('xmlkind', 'element')
+ if xmlkind == 'element':
+ prop += f"{INDENT}[XmlElement(\"{annotation_name}\")]\n"
+ elif xmlkind == 'attribute':
+ prop += f"{INDENT}[XmlAttribute(\"{annotation_name}\")]\n"
+
if self.system_text_json_annotation:
prop += f"{INDENT}[System.Text.Json.Serialization.JsonPropertyName(\"{annotation_name}\")]\n"
if is_enum_type:
@@ -527,7 +576,7 @@ def generate_property(self, field: Dict, class_name: str, parent_namespace: str)
prop += f"{INDENT}[System.Text.Json.Serialization.JsonConverter(typeof({field_type}))]\n"
if self.newtonsoft_json_annotation:
prop += f"{INDENT}[Newtonsoft.Json.JsonProperty(\"{annotation_name}\")]\n"
- prop += f"{INDENT}public {field_type} {field_name} {{ get; {'private ' if 'const' in field else ''}set; }}" + ((" = "+(f"\"{field_default}\"" if isinstance(field_default,str) else field_default) + ";") if field_default else "")
+ prop += f"{INDENT}public {field_type} {field_name} {{ get; set; }}" + ((" = "+(f"\"{field_default}\"" if isinstance(field_default,str) else field_default) + ";") if field_default else "")
return prop
def write_to_file(self, namespace: str, name: str, definition: str):
@@ -547,6 +596,8 @@ def write_to_file(self, namespace: str, name: str, definition: str):
file_content += "using System.Text.Json.Serialization;\n"
if self.newtonsoft_json_annotation:
file_content += "using Newtonsoft.Json;\n"
+ if self.system_xml_annotation: # Add XML serialization using directive
+ file_content += "using System.Xml.Serialization;\n"
if namespace:
# Namespace declaration with correct indentation for the definition
@@ -585,7 +636,10 @@ def generate_test_class(self, class_name: str, type_kind: str, test_directory_pa
test_class_name=test_class_name,
class_base_name=class_base_name,
fields=fields,
- avro_annotation=self.avro_annotation
+ avro_annotation=self.avro_annotation,
+ system_xml_annotation=self.system_xml_annotation,
+ system_text_json_annotation=self.system_text_json_annotation,
+ newtonsoft_json_annotation=self.newtonsoft_json_annotation
)
elif type_kind == "enum":
test_class_definition = process_template(
@@ -594,6 +648,10 @@ def generate_test_class(self, class_name: str, type_kind: str, test_directory_pa
test_class_name=test_class_name,
enum_base_name=class_base_name,
symbols=avro_schema.get('symbols', []),
+ avro_annotation=self.avro_annotation,
+ system_xml_annotation=self.system_xml_annotation,
+ system_text_json_annotation=self.system_text_json_annotation,
+ newtonsoft_json_annotation=self.newtonsoft_json_annotation
)
test_file_path = os.path.join(test_directory_path, f"{test_class_name}.cs")
@@ -658,6 +716,7 @@ def convert_schema(self, schema: JsonNode, output_dir: str):
project_name = self.base_namespace
self.schema_doc = schema
+ self.type_dict = build_flat_type_dict(self.schema_doc)
if not os.path.exists(output_dir):
os.makedirs(output_dir, exist_ok=True)
if not glob.glob(os.path.join(output_dir, "src", "*.sln")):
@@ -667,7 +726,14 @@ def convert_schema(self, schema: JsonNode, output_dir: str):
if not os.path.exists(os.path.dirname(sln_file)):
os.makedirs(os.path.dirname(sln_file))
with open(sln_file, 'w', encoding='utf-8') as file:
- file.write(process_template("avrotocsharp/project.sln.jinja", project_name=project_name, uuid=lambda:str(uuid.uuid4())))
+ file.write(process_template(
+ "avrotocsharp/project.sln.jinja",
+ project_name=project_name,
+ uuid=lambda:str(uuid.uuid4()),
+ avro_annotation=self.avro_annotation,
+ system_xml_annotation=self.system_xml_annotation,
+ system_text_json_annotation=self.system_text_json_annotation,
+ newtonsoft_json_annotation=self.newtonsoft_json_annotation))
if not glob.glob(os.path.join(output_dir, "src", "*.csproj")):
csproj_file = os.path.join(
output_dir, "src", f"{pascal(project_name)}.csproj")
@@ -675,7 +741,13 @@ def convert_schema(self, schema: JsonNode, output_dir: str):
if not os.path.exists(os.path.dirname(csproj_file)):
os.makedirs(os.path.dirname(csproj_file))
with open(csproj_file, 'w', encoding='utf-8') as file:
- file.write(process_template("avrotocsharp/project.csproj.jinja"))
+ file.write(process_template(
+ "avrotocsharp/project.csproj.jinja",
+ project_name=project_name,
+ avro_annotation=self.avro_annotation,
+ system_xml_annotation=self.system_xml_annotation,
+ system_text_json_annotation=self.system_text_json_annotation,
+ newtonsoft_json_annotation=self.newtonsoft_json_annotation))
if not glob.glob(os.path.join(output_dir, "test", "*.csproj")):
csproj_test_file = os.path.join(
output_dir, "test", f"{pascal(project_name)}.Test.csproj")
@@ -683,7 +755,13 @@ def convert_schema(self, schema: JsonNode, output_dir: str):
if not os.path.exists(os.path.dirname(csproj_test_file)):
os.makedirs(os.path.dirname(csproj_test_file))
with open(csproj_test_file, 'w', encoding='utf-8') as file:
- file.write(process_template("avrotocsharp/testproject.csproj.jinja", project_name=project_name))
+ file.write(process_template(
+ "avrotocsharp/testproject.csproj.jinja",
+ project_name=project_name,
+ avro_annotation=self.avro_annotation,
+ system_xml_annotation=self.system_xml_annotation,
+ system_text_json_annotation=self.system_text_json_annotation,
+ newtonsoft_json_annotation=self.newtonsoft_json_annotation))
self.output_dir = output_dir
for avro_schema in (avs for avs in schema if isinstance(avs, dict)):
@@ -697,14 +775,27 @@ def convert(self, avro_schema_path: str, output_dir: str):
self.convert_schema(schema, output_dir)
-def convert_avro_to_csharp(avro_schema_path, cs_file_path, base_namespace='', pascal_properties=False, system_text_json_annotation=False, newtonsoft_json_annotation=False, avro_annotation=False):
- """_summary_
-
- Converts Avro schema to C# classes
+def convert_avro_to_csharp(
+ avro_schema_path,
+ cs_file_path,
+ base_namespace='',
+ pascal_properties=False,
+ system_text_json_annotation=False,
+ newtonsoft_json_annotation=False,
+ system_xml_annotation=False, # New parameter
+ avro_annotation=False
+):
+ """Converts Avro schema to C# classes
Args:
- avro_schema_path (_type_): Avro input schema path
- cs_file_path (_type_): Output C# file path
+ avro_schema_path (str): Avro input schema path
+ cs_file_path (str): Output C# file path
+ base_namespace (str, optional): Base namespace. Defaults to ''.
+ pascal_properties (bool, optional): Pascal case properties. Defaults to False.
+ system_text_json_annotation (bool, optional): Use System.Text.Json annotations. Defaults to False.
+ newtonsoft_json_annotation (bool, optional): Use Newtonsoft.Json annotations. Defaults to False.
+ system_xml_annotation (bool, optional): Use System.Xml.Serialization annotations. Defaults to False.
+ avro_annotation (bool, optional): Use Avro annotations. Defaults to False.
"""
if not base_namespace:
@@ -713,27 +804,37 @@ def convert_avro_to_csharp(avro_schema_path, cs_file_path, base_namespace='', pa
avrotocs.pascal_properties = pascal_properties
avrotocs.system_text_json_annotation = system_text_json_annotation
avrotocs.newtonsoft_json_annotation = newtonsoft_json_annotation
+ avrotocs.system_xml_annotation = system_xml_annotation # Set the flag
avrotocs.avro_annotation = avro_annotation
avrotocs.convert(avro_schema_path, cs_file_path)
-def convert_avro_schema_to_csharp(avro_schema: JsonNode, output_dir: str, base_namespace: str = '', pascal_properties: bool = False, system_text_json_annotation: bool = False, newtonsoft_json_annotation: bool = False, avro_annotation: bool = False):
- """_summary_
-
- Converts Avro schema to C# classes
+def convert_avro_schema_to_csharp(
+ avro_schema: JsonNode,
+ output_dir: str,
+ base_namespace: str = '',
+ pascal_properties: bool = False,
+ system_text_json_annotation: bool = False,
+ newtonsoft_json_annotation: bool = False,
+ system_xml_annotation: bool = False, # New parameter
+ avro_annotation: bool = False
+):
+ """Converts Avro schema to C# classes
Args:
- avro_schema (_type_): Avro schema to convert
- output_dir (_type_): Output directory
- base_namespace (_type_): Base namespace for the generated classes
- pascal_properties (_type_): Pascal case properties
- system_text_json_annotation (_type_): Use System.Text.Json annotations
- newtonsoft_json_annotation (_type_): Use Newtonsoft.Json annotations
- avro_annotation (_type_): Use Avro annotations
+ avro_schema (JsonNode): Avro schema to convert
+ output_dir (str): Output directory
+ base_namespace (str, optional): Base namespace for the generated classes. Defaults to ''.
+ pascal_properties (bool, optional): Pascal case properties. Defaults to False.
+ system_text_json_annotation (bool, optional): Use System.Text.Json annotations. Defaults to False.
+ newtonsoft_json_annotation (bool, optional): Use Newtonsoft.Json annotations. Defaults to False.
+ system_xml_annotation (bool, optional): Use System.Xml.Serialization annotations. Defaults to False.
+ avro_annotation (bool, optional): Use Avro annotations. Defaults to False.
"""
avrotocs = AvroToCSharp(base_namespace)
avrotocs.pascal_properties = pascal_properties
avrotocs.system_text_json_annotation = system_text_json_annotation
avrotocs.newtonsoft_json_annotation = newtonsoft_json_annotation
+ avrotocs.system_xml_annotation = system_xml_annotation # Set the flag
avrotocs.avro_annotation = avro_annotation
avrotocs.convert_schema(avro_schema, output_dir)
diff --git a/avrotize/avrotocsharp/class_test.cs.jinja b/avrotize/avrotocsharp/class_test.cs.jinja
index 3e72e46..ffd3b09 100644
--- a/avrotize/avrotocsharp/class_test.cs.jinja
+++ b/avrotize/avrotocsharp/class_test.cs.jinja
@@ -56,14 +56,24 @@ public class {{ test_class_name }}
{%- if avro_annotation %}
/// Testing Avro serializer
[Test]
- public void Test_ToByteArray_FromData()
+ public void Test_ToByteArray_FromData_Avro()
{
var mediaType = "application/vnd.apache.avro+avro";
var bytes = _instance.ToByteArray(mediaType);
var newInstance = {{ class_base_name }}.FromData(bytes, mediaType);
_instance.Should().BeEquivalentTo(newInstance);
}
-
+ {%- endif %}
+ {%- if system_xml_annotation %}
+ /// Testing XML serializer
+ [Test]
+ public void Test_ToByteArray_FromData_Xml()
+ {
+ var mediaType = "application/xml";
+ var bytes = _instance.ToByteArray(mediaType);
+ var newInstance = {{ class_base_name }}.FromData(bytes, mediaType);
+ _instance.Should().BeEquivalentTo(newInstance);
+ }
{%- endif %}
}
{% endfilter %}
diff --git a/avrotize/avrotocsharp/dataclass_core.jinja b/avrotize/avrotocsharp/dataclass_core.jinja
index e753b3d..e7c32ba 100644
--- a/avrotize/avrotocsharp/dataclass_core.jinja
+++ b/avrotize/avrotocsharp/dataclass_core.jinja
@@ -1,4 +1,4 @@
- {%- if avro_annotation or system_text_json_annotation or newtonsoft_json_annotation %}
+ {%- if avro_annotation or system_text_json_annotation or newtonsoft_json_annotation or system_xml_annotation %}
///
/// Creates an object from the data
///
@@ -11,7 +11,7 @@
if ( data is {{ class_name }}) return ({{ class_name }})data;
if ( contentTypeString == null ) contentTypeString = System.Net.Mime.MediaTypeNames.Application.Octet;
var contentType = new System.Net.Mime.ContentType(contentTypeString);
- {%- if avro_annotation or system_text_json_annotation or newtonsoft_json_annotation %}
+ {%- if avro_annotation or system_text_json_annotation or newtonsoft_json_annotation or system_xml_annotation %}
if ( contentType.MediaType.EndsWith("+gzip"))
{
var stream = data switch
@@ -87,10 +87,37 @@
return ((System.BinaryData)data).ToObjectFromJson<{{ class_name }}>();
}
}
+ {%- endif %}
+ {%- if system_xml_annotation %}
+ if ( contentType.MediaType.StartsWith(System.Net.Mime.MediaTypeNames.Text.Xml) || contentType.MediaType.StartsWith(System.Net.Mime.MediaTypeNames.Application.Xml) || contentType.MediaType.EndsWith("+xml"))
+ {
+ var serializer = new System.Xml.Serialization.XmlSerializer(typeof({{ class_name }}));
+ if (data is string)
+ {
+ using (var reader = new System.IO.StringReader((string)data))
+ {
+ return ({{ class_name }}?)serializer.Deserialize(reader);
+ }
+ }
+ else if (data is System.IO.Stream)
+ {
+ return ({{ class_name }}?)serializer.Deserialize((System.IO.Stream)data);
+ }
+ else if (data is System.BinaryData)
+ {
+ var memoryStream = new System.IO.MemoryStream(((System.BinaryData)data).ToArray());
+ return ({{ class_name }}?)serializer.Deserialize(memoryStream);
+ }
+ else if (data is byte[])
+ {
+ var memoryStream = new System.IO.MemoryStream((byte[])data);
+ return ({{ class_name }}?)serializer.Deserialize(memoryStream);
+ }
+ }
{%- endif %}
throw new System.NotSupportedException($"Unsupported media type {contentType.MediaType}");
}
- {%-endif %}
+ {%- endif %}
{%- if avro_annotation %}
private class SpecificDatumWriter : global::Avro.Specific.SpecificDatumWriter<{{ class_name }}>
@@ -101,7 +128,15 @@
protected override WriteItem ResolveEnum(global::Avro.EnumSchema es)
{
- return base.ResolveEnum(global::Avro.EnumSchema.Create(es.Name, es.Symbols, GetType().Assembly.GetName().Name+"."+es.Namespace, null, null, es.Documentation, es.Default));
+ var enumType = GetType().Assembly.GetType(GetType().Assembly.GetName().Name+"."+es.Namespace + "." + es.Name, false, true);
+ if (enumType != null)
+ {
+ return base.ResolveEnum(global::Avro.EnumSchema.Create(enumType.Name, es.Symbols, enumType.Namespace, null, null, es.Documentation, es.Default));
+ }
+ else
+ {
+ return base.ResolveEnum(es);
+ }
}
}
{%- endif %}
@@ -109,7 +144,7 @@
{%- if avro_annotation %}
{%- endif%}
- {%- if avro_annotation or system_text_json_annotation or newtonsoft_json_annotation %}
+ {%- if avro_annotation or system_text_json_annotation or newtonsoft_json_annotation or system_xml_annotation %}
///
/// Converts the object to a byte array
///
@@ -151,7 +186,22 @@
result = System.Text.Encoding.GetEncoding(contentType.CharSet??"utf-8").GetBytes(Newtonsoft.Json.JsonConvert.SerializeObject(this));
}
{%- endif %}
- {%- if avro_annotation or system_text_json_annotation or newtonsoft_json_annotation %}
+ {%- if system_xml_annotation %}
+ if (contentType.MediaType.StartsWith(System.Net.Mime.MediaTypeNames.Text.Xml) || contentType.MediaType.StartsWith(System.Net.Mime.MediaTypeNames.Application.Xml) || contentType.MediaType.EndsWith("+xml"))
+ {
+ var serializer = new System.Xml.Serialization.XmlSerializer(typeof({{ class_name }}));
+ using (var stream = new System.IO.MemoryStream())
+ {
+ using (var writer = new System.IO.StreamWriter(stream))
+ {
+ serializer.Serialize(writer, this);
+ writer.Flush();
+ result = stream.ToArray();
+ }
+ }
+ }
+ {%- endif %}
+ {%- if avro_annotation or system_text_json_annotation or newtonsoft_json_annotation or system_xml_annotation %}
if (result != null && contentType.MediaType.EndsWith("+gzip"))
{
var stream = new System.IO.MemoryStream();
@@ -166,7 +216,7 @@
}
{%- endif %}
- {%- if system_text_json_annotation %}
+ {%- if system_text_json_annotation or newtonsoft_json_annotation %}
///
/// Checks if the JSON element matches the schema
///
diff --git a/avrotize/avrotocsharp/project.csproj.jinja b/avrotize/avrotocsharp/project.csproj.jinja
index 5d4e039..df5b609 100644
--- a/avrotize/avrotocsharp/project.csproj.jinja
+++ b/avrotize/avrotocsharp/project.csproj.jinja
@@ -5,9 +5,15 @@
true
+ {%- if avro_annotation %}
+ {%- endif %}
+ {%- if newtonsoft_json_annotation %}
+ {%- endif %}
+ {%- if system_text_json_annotation %}
+ {%- endif %}
diff --git a/avrotize/avrotots/class_core.ts.jinja b/avrotize/avrotots/class_core.ts.jinja
index 774ad7b..a3d8b3d 100644
--- a/avrotize/avrotots/class_core.ts.jinja
+++ b/avrotize/avrotots/class_core.ts.jinja
@@ -107,6 +107,8 @@ export class {{ class_name }} {
throw new Error(`Unsupported media type: ${contentTypeString}`);
}
+
+ {%- if typed_json_annotation %}
public static isJsonMatch(element: any): boolean {
{%- if fields|length == 0 %}
return true;
@@ -120,4 +122,5 @@ export class {{ class_name }} {
{%- endif %}
}
{%- endif %}
+ {%- endif %}
}
diff --git a/avrotize/avrotots/enum_core.ts.jinja b/avrotize/avrotots/enum_core.ts.jinja
index f094ba5..fa272a2 100644
--- a/avrotize/avrotots/enum_core.ts.jinja
+++ b/avrotize/avrotots/enum_core.ts.jinja
@@ -38,7 +38,9 @@ export class {{ enum_name }}Utils {
}
}
+ {%- if typed_json_annotation %}
static isJsonMatch(value: string): boolean {
return Object.values({{ enum_name }}).includes(value as {{ enum_name }});
}
+ {%- endif %}
}
\ No newline at end of file
diff --git a/avrotize/commands.json b/avrotize/commands.json
index 7df8861..6117dd8 100644
--- a/avrotize/commands.json
+++ b/avrotize/commands.json
@@ -899,6 +899,7 @@
"avro_annotation": "args.avro_annotation",
"system_text_json_annotation": "args.system_text_json_annotation",
"newtonsoft_json_annotation": "args.newtonsoft_json_annotation",
+ "system_xml_annotation": "args.system_xml_annotation",
"pascal_properties": "args.pascal_properties",
"base_namespace": "args.namespace"
}
@@ -944,6 +945,13 @@
"default": false,
"required": false
},
+ {
+ "name": "--system-xml-annotation",
+ "type": "bool",
+ "help": "Use System.Xml annotations",
+ "default": false,
+ "required": false
+ },
{
"name": "--newtonsoft-json-annotation",
"type": "bool",
diff --git a/avrotize/xsdtoavro.py b/avrotize/xsdtoavro.py
index 408195a..6b36cbd 100644
--- a/avrotize/xsdtoavro.py
+++ b/avrotize/xsdtoavro.py
@@ -22,6 +22,7 @@ def __init__(self) -> None:
""" Initialize the class. """
self.simple_type_map: Dict[str, str | dict] = {}
self.avro_namespace = ''
+ self.xml_namespace = ''
def xsd_targetnamespace_to_avro_namespace(self, targetnamespace: str) -> str:
"""Convert a XSD namespace to Avro Namespace."""
@@ -303,6 +304,7 @@ def process_top_level_element(self, element: ET.Element, namespaces: dict):
'type': 'record',
'name': 'Root',
'namespace': self.avro_namespace,
+ 'xmlns': self.xml_namespace,
'fields': []
}
annotation = element.find(f'{{{XSD_NAMESPACE}}}annotation', namespaces)
@@ -349,6 +351,7 @@ def xsd_to_avro(self, xsd_path: str, code_namespace: str | None = None):
target_namespace = root.get('targetNamespace')
if target_namespace is None:
raise ValueError('targetNamespace not found')
+ self.xml_namespace = target_namespace
if not code_namespace:
self.avro_namespace = self.xsd_targetnamespace_to_avro_namespace(target_namespace)
else:
diff --git a/csharpcodegen.md b/csharpcodegen.md
index 8ff0932..7b7b237 100644
--- a/csharpcodegen.md
+++ b/csharpcodegen.md
@@ -2,10 +2,10 @@
Avrotize is can generates C# classes from Avro schema files with the "a2csharp" command. The generated classes reflect the type model described by the Avro schema.
-With the `avro_annotation` option, the code generator is an alternative to the `avrogen` tool provided by the Avro project. Unlike `avrogen`, Avrotize generates classes that directly support type unions and it allows combining Avro annotations
+With the `--avro-annotation` option, the code generator is an alternative to the `avrogen` tool provided by the Avro project. Unlike `avrogen`, Avrotize generates classes that directly support type unions and it allows combining Avro annotations
with annotations for `System.Text.Json` serialization.
-With the `system_text_json_annotation` option, the code generator emits annotations for `System.Text.Json` serialization. This option can be used standalone and is not dependent on the `avro_annotation` option, which means that Avro Schema
+With the `--system-text-json-annotation` option, the code generator emits annotations for `System.Text.Json` serialization. This option can be used standalone and is not dependent on the `--avro-annotation` option, which means that Avro Schema
can be used to generate classes with `System.Text.Json` serialization annotations as an alternative to JSON Schema, without
the Avro serialization framework being required. The generated classes fully support type unions (equivalent to JSN Schema's `oneOf`) without requiring a "discriminator" field, but rather deduce the type from the serialized data's structure.
@@ -46,7 +46,7 @@ the Avro serialization framework being required. The generated classes fully sup
```
The following is an example of the generated code for the schema above, with the
-`avro_annotation` option and the `system_text_json_annotation` option turned on.
+`--avro-annotation` option and the `--system-text-json-annotation` option turned on.
We will discuss the generated code in detail below and which parts are generated
by which option.
@@ -94,7 +94,7 @@ The generated code includes a class declaration for the record type defined in
the Avro schema. The class name is derived from the name of the record type in
the Avro schema.
-If the `avro_annotation` option is provided, the class implements the
+If the `--avro-annotation` option is provided, the class implements the
`ISpecificRecord` interface from the Avro library. The interface provides
methods to access the fields of the record and to convert the record to and from
byte arrays. The interface is used by the Avro serialization framework to
@@ -145,7 +145,7 @@ The mapping for Avro types to C# types is as follows:
| timestamp-micros | DateTime |
| duration | TimeSpan |
-If the `system_text_json_annotation` option is used, fields are annotated with
+If the `--system-text-json-annotation` option is used, fields are annotated with
the `JsonPropertyName` attribute from the `System.Text.Json.Serialization`
namespace. The attribute specifies the name of the field in the JSON
representation of the record.
@@ -200,7 +200,7 @@ constructor initializes the fields of the record to their default values.
### Constructor from Avro GenericRecord
-If the `avro_annotation` option is used, the generated code includes a
+If the `--avro-annotation` option is used, the generated code includes a
constructor that takes an Avro `GenericRecord` object as a parameter. The
constructor initializes the fields of the record from the values in the
`GenericRecord`.
@@ -222,7 +222,7 @@ constructor initializes the fields of the record from the values in the
### Avro Schema
-If the `avro_annotation` option is used, the generated code includes a static
+If the `--avro-annotation` option is used, the generated code includes a static
field that contains the Avro schema for the record type. The schema is parsed
from a JSON string that represents the schema.
@@ -240,7 +240,7 @@ from a JSON string that represents the schema.
### ISpecificRecord Interface Implementation
-If the `avro_annotation` option is used, the generated code includes
+If the `--avro-annotation` option is used, the generated code includes
implementations of the `Get` and `Put` methods from the Avro framework's
`ISpecificRecord` interface. The methods provide access to the fields of the
record and allow the record to be serialized and deserialized by the Avro
@@ -277,7 +277,7 @@ serialization framework.
### ToByteArray Method
-If either or both the `avro_annotation` and `system_text_json_annotation`
+If either or both the `--avro-annotation` and `--system-text-json-annotation`
options are used, the generated code includes a `ToByteArray` method that
converts the record to a byte array. The method takes a content type string as a
parameter that specifies the encoding of the data. The method encodes the record
@@ -287,14 +287,14 @@ The following encodings are supported:
| Enabled Option | Content Type String | Encoding |
|----------------|---------------------|----------|
-| avro_annotation | `avro/binary` | Avro binary encoding |
-| avro_annotation | `avro/vnd.apache.avro+avro` | Avro binary encoding |
-| avro_annotation | `avro/vnd.apache.avro+avro+gzip` | Avro binary encoding with GZIP compression |
-| avro_annotation | `avro/json` | Avro JSON encoding |
-| avro_annotation | `application/vnd.apache.avro+json` | Avro JSON encoding |
-| avro_annotation | `avro/vnd.apache.avro+json+gzip` | Avro JSON encoding with GZIP compression |
-| system_text_json_annotation | `application/json` | JSON encoding |
-| system_text_json_annotation | `application/json+gzip` | JSON encoding with GZIP compression |
+| --avro-annotation | `avro/binary` | Avro binary encoding |
+| --avro-annotation | `avro/vnd.apache.avro+avro` | Avro binary encoding |
+| --avro-annotation | `avro/vnd.apache.avro+avro+gzip` | Avro binary encoding with GZIP compression |
+| --avro-annotation | `avro/json` | Avro JSON encoding |
+| --avro-annotation | `application/vnd.apache.avro+json` | Avro JSON encoding |
+| --avro-annotation | `avro/vnd.apache.avro+json+gzip` | Avro JSON encoding with GZIP compression |
+| --system-text-json-annotation | `application/json` | JSON encoding |
+| --system-text-json-annotation | `application/json+gzip` | JSON encoding with GZIP compression |
```csharp
@@ -347,7 +347,7 @@ The following encodings are supported:
### FromData Method
-If either or both the `avro_annotation` and `system_text_json_annotation`
+If either or both the `--avro-annotation` and `--system-text-json-annotation`
options are used, the generated code includes a `FromData` method that converts
a byte array to a record object. The method takes the encoded data and a content
type string as parameters and returns the decoded record object.
@@ -444,7 +444,7 @@ the decoded record object.
### IsJsonMatch Method
-If the `system_text_json_annotation` option is used, the generated code includes
+If the `--system-text-json-annotation` option is used, the generated code includes
an `IsJsonMatch` method that checks if a JSON element matches the schema. The
method takes a `JsonElement` object as a parameter and returns a boolean value
that indicates whether the JSON element matches the schema.
@@ -548,7 +548,7 @@ In this case:
The following generated code refers to the `DocumentUnion` class that is
produced for the `document` field shown above. If the
-`system_text_json_annotation` option is used, the union class is annotated with
+`--system-text-json-annotation` option is used, the union class is annotated with
the `JsonConverter` attribute from the `System.Text.Json.Serialization`
namespace. The attribute specifies the converter class that is used to serialize
and deserialize the union.
@@ -565,7 +565,7 @@ and deserialize the union.
The `DocumentUnion` class is embedded in the generated record class, into a
separate file, but into the same partial class.
-If the `system_text_json_annotation` option is used, the `DocumentUnion` class
+If the `--system-text-json-annotation` option is used, the `DocumentUnion` class
is derived from the `JsonConverter` class from the
`System.Text.Json.Serialization` namespace. The class provides methods to
serialize and deserialize the union.
@@ -613,7 +613,7 @@ union value that corresponds to the type of the object. This is a factory method
instead of a constructor to disambiguate from generated constructors for maps,
which are also represented as `Object` in some cases.
-If the `avro_annotation` option is used, the method checks whether the object is
+If the `--avro-annotation` option is used, the method checks whether the object is
a GenericRecord and creates a union value from the GenericRecord with the respective
constructor.
@@ -644,7 +644,7 @@ constructor.
### Constructor from Avro GenericRecord
-If the `avro_annotation` option is used, the generated code includes a
+If the `--avro-annotation` option is used, the generated code includes a
constructor that takes an Avro `GenericRecord` object as a parameter. The
constructor initializes the fields of the union from the values in the
`GenericRecord`.
@@ -737,7 +737,7 @@ void global::Avro.Specific.ISpecificRecord.Put(int fieldPos, object fieldValue)
### Read Method
-If the `system_text_json_annotation` option is used, the generated code includes a
+If the `--system-text-json-annotation` option is used, the generated code includes a
`Read` method that reads the JSON representation of the object. The method takes a
`Utf8JsonReader` object as a parameter and returns the union value that corresponds
to the JSON data.
@@ -768,7 +768,7 @@ constructor.
### Write Method
-If the `system_text_json_annotation` option is used, the generated code includes a
+If the `--system-text-json-annotation` option is used, the generated code includes a
`Write` method that writes the JSON representation of the object. The method takes a
`Utf8JsonWriter` object as a parameter and writes the JSON representation of the union value.
@@ -798,7 +798,7 @@ that corresponds to the type.
### IsJsonMatch Method
-If the `system_text_json_annotation` option is used, the generated code includes an
+If the `--system-text-json-annotation` option is used, the generated code includes an
`IsJsonMatch` method that checks if a JSON element matches the schema. The method
takes a `JsonElement` object as a parameter and returns a boolean value that indicates
whether the JSON element matches the schema.
@@ -938,7 +938,7 @@ a nullable field.
The `Test1Union` class is generated for the `test1` field. As you may expect
from the previous example, the union class has constructors for each type in
the union, a `ToObject` method, and the `Read`, `Write`, and `IsJsonMatch`
-methods generated if 'system_text_json_annotation' is used.
+methods generated if '--system-text-json-annotation' is used.
```csharp
#pragma warning disable CS8618
diff --git a/test/test_avrotocsharp.py b/test/test_avrotocsharp.py
index 14bfd37..ca1b09f 100644
--- a/test/test_avrotocsharp.py
+++ b/test/test_avrotocsharp.py
@@ -20,7 +20,7 @@
class TestAvroToCSharp(unittest.TestCase):
- def run_convert_avsc_to_csharp(self, avsc_name, system_text_json_annotation=False, newtonsoft_json_annotation=False, avro_annotation=False, pascal_properties=False):
+ def run_convert_avsc_to_csharp(self, avsc_name, system_text_json_annotation=False, newtonsoft_json_annotation=False, avro_annotation=False, system_xml_annotation=False, pascal_properties=False):
""" Test converting an avsc file to C# """
cwd = os.getcwd()
avro_path = os.path.join(cwd, "test", "avsc", avsc_name + ".avsc")
@@ -29,33 +29,41 @@ def run_convert_avsc_to_csharp(self, avsc_name, system_text_json_annotation=Fals
shutil.rmtree(cs_path, ignore_errors=True)
os.makedirs(cs_path, exist_ok=True)
- convert_avro_to_csharp(avro_path, cs_path, pascal_properties=pascal_properties, system_text_json_annotation=system_text_json_annotation, newtonsoft_json_annotation=newtonsoft_json_annotation, avro_annotation=avro_annotation)
+ convert_avro_to_csharp(avro_path, cs_path, pascal_properties=pascal_properties, system_text_json_annotation=system_text_json_annotation, newtonsoft_json_annotation=newtonsoft_json_annotation, avro_annotation=avro_annotation, system_xml_annotation=system_xml_annotation)
assert subprocess.check_call(
- ['dotnet', 'build'], cwd=cs_path, stdout=sys.stdout, stderr=sys.stderr) == 0
+ ['dotnet', 'test'], cwd=cs_path, stdout=sys.stdout, stderr=sys.stderr) == 0
def test_convert_address_avsc_to_csharp(self):
""" Test converting an address.avsc file to C# """
self.run_convert_avsc_to_csharp("address")
- def test_convert_address_avsc_to_csharp_avro_annotation(self):
- """ Test converting an address.avsc file to C# """
- self.run_convert_avsc_to_csharp("address", avro_annotation=True)
+ def test_convert_address_avsc_to_csharp_annotated(self):
+ for system_text_json_annotation in [True, False]:
+ for newtonsoft_json_annotation in [True, False]:
+ for avro_annotation in [True, False]:
+ for pascal_properties in [True, False]:
+ for system_xml_annotation in [True, False]:
+ self.run_convert_avsc_to_csharp("address", system_text_json_annotation=system_text_json_annotation, newtonsoft_json_annotation=newtonsoft_json_annotation, avro_annotation=avro_annotation, pascal_properties=pascal_properties, system_xml_annotation=system_xml_annotation)
- def test_convert_address_avsc_to_csharp_system_text_json_annotation(self):
- """ Test converting an address.avsc file to C# """
- self.run_convert_avsc_to_csharp("address", system_text_json_annotation=True)
- def test_convert_enumfield_avsc_to_csharp_system_text_json_annotation(self):
- """ Test converting an address.avsc file to C# """
- self.run_convert_avsc_to_csharp("enumfield", system_text_json_annotation=True)
-
- def test_convert_address_avsc_to_csharp_newtonsoft_json_annotation(self):
- """ Test converting an address.avsc file to C# """
- self.run_convert_avsc_to_csharp("address", newtonsoft_json_annotation=True)
-
- def test_convert_telemetry_avsc_to_csharp(self):
- """ Test converting a telemetry.avsc file to C# """
- self.run_convert_avsc_to_csharp("telemetry")
+ def test_convert_enumfield_avsc_to_csharp_annotated(self):
+ """ Test converting an enumfield.avsc file to C# """
+ for system_text_json_annotation in [True, False]:
+ for newtonsoft_json_annotation in [True, False]:
+ for avro_annotation in [True, False]:
+ for pascal_properties in [True, False]:
+ for system_xml_annotation in [True, False]:
+ self.run_convert_avsc_to_csharp("enumfield", system_text_json_annotation=system_text_json_annotation, newtonsoft_json_annotation=newtonsoft_json_annotation, avro_annotation=avro_annotation, pascal_properties=pascal_properties, system_xml_annotation=system_xml_annotation)
+
+
+ def test_convert_telemetry_avsc_to_csharp_annotated(self):
+ """ Test converting an telemetry.avsc file to C# """
+ for system_text_json_annotation in [True, False]:
+ for newtonsoft_json_annotation in [True, False]:
+ for avro_annotation in [True, False]:
+ for pascal_properties in [True, False]:
+ for system_xml_annotation in [True, False]:
+ self.run_convert_avsc_to_csharp("telemetry", system_text_json_annotation=system_text_json_annotation, newtonsoft_json_annotation=newtonsoft_json_annotation, avro_annotation=avro_annotation, pascal_properties=pascal_properties, system_xml_annotation=system_xml_annotation)
def test_convert_address_nn_avsc_to_csharp(self):
""" Test converting an address.nn.avsc file to C# """
@@ -81,7 +89,7 @@ def test_convert_twotypeunion_ann_avsc_to_csharp(self):
assert subprocess.check_call(
['dotnet', 'run', '--force'], cwd=cs_test_path, stdout=sys.stdout, stderr=sys.stderr) == 0
- def run_test_convert_twotypeunion_avsc_to_csharp(self, system_text_json_annotation=True, newtonsoft_json_annotation=True, avro_annotation=True, pascal_properties=True):
+ def run_test_convert_twotypeunion_avsc_to_csharp(self, system_text_json_annotation=True, newtonsoft_json_annotation=True, avro_annotation=True, pascal_properties=True, system_xml_annotation=True):
""" Test converting a twotypeunion.avsc file to C# """
cwd = os.getcwd()
avro_path = os.path.join(cwd, "test", "avsc", "twotypeunion.avsc")
@@ -90,20 +98,19 @@ def run_test_convert_twotypeunion_avsc_to_csharp(self, system_text_json_annotati
shutil.rmtree(cs_path, ignore_errors=True)
os.makedirs(cs_path, exist_ok=True)
- convert_avro_to_csharp(avro_path, cs_path, base_namespace="TwoTypeUnion", system_text_json_annotation=system_text_json_annotation, newtonsoft_json_annotation=newtonsoft_json_annotation, avro_annotation=avro_annotation, pascal_properties=pascal_properties)
+ convert_avro_to_csharp(avro_path, cs_path, base_namespace="TwoTypeUnion", system_text_json_annotation=system_text_json_annotation, newtonsoft_json_annotation=newtonsoft_json_annotation, avro_annotation=avro_annotation, pascal_properties=pascal_properties, system_xml_annotation=system_xml_annotation)
assert subprocess.check_call(
['dotnet', 'build'], cwd=cs_path, stdout=sys.stdout, stderr=sys.stderr) == 0
- def test_convert_twotypeunion_avsc_to_csharp_system_text_json_annotation(self):
+ def test_convert_twotypeunion_avsc_to_csharp_annotated(self):
""" Test converting a twotypeunion.avsc file to C# """
- self.run_test_convert_twotypeunion_avsc_to_csharp(system_text_json_annotation=True, newtonsoft_json_annotation=False, avro_annotation=False)
- self.run_test_convert_twotypeunion_avsc_to_csharp(system_text_json_annotation=True, newtonsoft_json_annotation=True, avro_annotation=False)
- self.run_test_convert_twotypeunion_avsc_to_csharp(system_text_json_annotation=True, newtonsoft_json_annotation=False, avro_annotation=True)
- self.run_test_convert_twotypeunion_avsc_to_csharp(system_text_json_annotation=True, newtonsoft_json_annotation=True, avro_annotation=True)
- self.run_test_convert_twotypeunion_avsc_to_csharp(system_text_json_annotation=False, newtonsoft_json_annotation=False, avro_annotation=False)
- self.run_test_convert_twotypeunion_avsc_to_csharp(system_text_json_annotation=False, newtonsoft_json_annotation=True, avro_annotation=False)
- self.run_test_convert_twotypeunion_avsc_to_csharp(system_text_json_annotation=False, newtonsoft_json_annotation=False, avro_annotation=True)
- self.run_test_convert_twotypeunion_avsc_to_csharp(system_text_json_annotation=False, newtonsoft_json_annotation=True, avro_annotation=True)
+ for system_text_json_annotation in [True, False]:
+ for newtonsoft_json_annotation in [True, False]:
+ for avro_annotation in [True, False]:
+ for pascal_properties in [True, False]:
+ for system_xml_annotation in [True, False]:
+ self.run_test_convert_twotypeunion_avsc_to_csharp(system_text_json_annotation=system_text_json_annotation, newtonsoft_json_annotation=newtonsoft_json_annotation, avro_annotation=avro_annotation, pascal_properties=pascal_properties, system_xml_annotation=system_xml_annotation)
+
def test_convert_typemapunion_avsc_to_csharp(self):
""" Test converting an address.avsc file to C# """
@@ -179,5 +186,5 @@ def test_convert_jfrog_pipelines_jsons_to_avro_to_csharp_annotated(self):
convert_jsons_to_avro(jsons_path, avro_path)
convert_avro_to_csharp(avro_path, cs_path, pascal_properties=True, avro_annotation=True,
- system_text_json_annotation=True, newtonsoft_json_annotation=True)
+ system_text_json_annotation=True, newtonsoft_json_annotation=True, system_xml_annotation=True)
assert subprocess.check_call(['dotnet', 'build'], cwd=cs_path, stdout=sys.stdout, stderr=sys.stderr) == 0
diff --git a/vscode/avrotize/src/extension.ts b/vscode/avrotize/src/extension.ts
index ec6c505..2140729 100644
--- a/vscode/avrotize/src/extension.ts
+++ b/vscode/avrotize/src/extension.ts
@@ -312,11 +312,13 @@ export function activate(context: vscode.ExtensionContext) {
const avro_annotation_value_arg = avro_annotation_value ? '--avro-annotation' : '';
const system_text_json_annotation_value = await vscode.window.showQuickPick(['Yes', 'No'], { title: 'Use System.Text.Json annotations?' }) === 'Yes';
const system_text_json_annotation_value_arg = system_text_json_annotation_value ? '--system_text_json_annotation' : '';
+ const system_xml_annotation_value = await vscode.window.showQuickPick(['Yes', 'No'], { title: 'Use System.Xml annotations?' }) === 'Yes';
+ const system_xml_annotation_value_arg = system_xml_annotation_value ? '--system_xml_annotation' : '';
const pascal_properties_value = await vscode.window.showQuickPick(['Yes', 'No'], { title: 'Use PascalCase properties?' }) === 'Yes';
const pascal_properties_value_arg = pascal_properties_value ? '--pascal-properties' : '';
const outputPath = await vscode.window.showSaveDialog({ defaultUri: vscode.Uri.file(outputPathSuggestion), saveLabel: 'Save Output', filters : { 'All Files': ['*'] } });
if (!outputPath) { return; }
- const command = `avrotize a2cs ${filePath} --out ${outputPath.fsPath} ${namespace_value_arg} ${avro_annotation_value_arg} ${system_text_json_annotation_value_arg} ${pascal_properties_value_arg}`;
+ const command = `avrotize a2cs ${filePath} --out ${outputPath.fsPath} ${namespace_value_arg} ${avro_annotation_value_arg} ${system_text_json_annotation_value_arg} ${system_xml_annotation_value_arg} ${pascal_properties_value_arg}`;
executeCommand(command, outputPath, outputChannel);
}));