diff --git a/.editorconfig b/.editorconfig
index 6199b4c..b14bca5 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,3 +1,5 @@
+root = true
+
[*]
charset = utf-8
end_of_line = lf
@@ -7,24 +9,16 @@ indent_style = space
indent_size = 4 # A property with the same name was updated with a value 2 in a section [{*.yaml,*.yml}]
[{*.yaml,*.yml}]
-indent_style = space
indent_size = 2 # A property with the same name was updated with a value 4 in a section [*]; with a value 4 in a section [*.{appxmanifest,asax,ascx,aspx,axaml,build,cg,cginc,compute,cs,cshtml,dtd,hlsl,hlsli,hlslinc,master,nuspec,paml,razor,resw,resx,skin,usf,ush,vb,xaml,xamlx,xoml,xsd}]
-dotnet_diagnostic.CA1047.severity = error
[*.{appxmanifest,asax,ascx,aspx,axaml,build,cg,cginc,compute,cs,cshtml,dtd,hlsl,hlsli,hlslinc,master,nuspec,paml,razor,resw,resx,skin,usf,ush,vb,xaml,xamlx,xoml,xsd}]
-indent_style = space
-indent_size = 4 # A property with the same name was updated with a value 2 in a section [{*.yaml,*.yml}]
tab_width = 4
-dotnet_style_operator_placement_when_wrapping = beginning_of_line
+
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
-dotnet_style_prefer_is_null_check_over_reference_equality_method = true:error
-dotnet_style_prefer_auto_properties = true:warning
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
-dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
-dotnet_style_prefer_conditional_expression_over_return = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
@@ -35,13 +29,8 @@ dotnet_style_namespace_match_folder = true:suggestion
dotnet_style_readonly_field = true:warning
dotnet_style_predefined_type_for_locals_parameters_members = true:warning
dotnet_style_predefined_type_for_member_access = true:warning
-dotnet_style_require_accessibility_modifiers = always:error
dotnet_style_allow_multiple_blank_lines_experimental = false:silent
dotnet_style_allow_statement_immediately_after_block_experimental = false:silent
-dotnet_code_quality_unused_parameters = all:warning
-dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:warning
-dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:warning
-dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:warning
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
dotnet_style_qualification_for_field = false:silent
dotnet_style_qualification_for_property = false:silent
@@ -77,6 +66,8 @@ dotnet_diagnostic.CA1852.severity = suggestion
dotnet_diagnostic.CA2012.severity = warning
dotnet_diagnostic.CA2019.severity = warning
dotnet_diagnostic.CA2211.severity = warning
+dotnet_diagnostic.CA1822.severity = suggestion
+dotnet_diagnostic.CA1725.severity = suggestion
[*.cs]
@@ -104,7 +95,8 @@ dotnet_style_parentheses_in_other_operators = never_if_unnecessary
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:warning
# Modifier preferences
-dotnet_style_require_accessibility_modifiers = always:error
+dotnet_style_require_accessibility_modifiers = for_non_interface_members:warning
+dotnet_diagnostics.IDE0040.severity = warning
# Expression-level preferences
dotnet_style_coalesce_expression = true
@@ -712,14 +704,16 @@ resharper_blank_lines_inside_region = 1
resharper_blank_lines_inside_type = 0
resharper_blank_line_after_pi = true
resharper_braces_for_dowhile = required
-resharper_braces_for_fixed = required
+resharper_braces_for_fixed = required_for_multiline
resharper_braces_for_for = required_for_multiline
resharper_braces_for_foreach = required_for_multiline
resharper_braces_for_ifelse = required_for_multiline
-resharper_braces_for_lock = required
-resharper_braces_for_using = required
+resharper_braces_for_lock = required_for_multiline
+resharper_braces_for_using = required_for_multiline
resharper_braces_for_while = required_for_multiline
resharper_braces_redundant = true
+resharper_builtin_type_apply_to_native_integer = true
+resharper_csharp_empty_block_style = together_same_line
resharper_break_template_declaration = line_break
resharper_can_use_global_alias = false
resharper_configure_await_analysis_mode = disabled
@@ -817,7 +811,7 @@ resharper_indent_typearg_angles = inside
resharper_indent_typeparam_angles = inside
resharper_indent_type_constraints = true
resharper_indent_wrapped_function_names = false
-resharper_instance_members_qualify_declared_in = base_class
+resharper_instance_members_qualify_declared_in =
resharper_int_align_assignments = false
resharper_int_align_binary_expressions = false
resharper_int_align_declaration_names = false
@@ -918,7 +912,7 @@ resharper_place_simple_switch_expression_on_single_line = false
resharper_place_type_attribute_on_same_line = false
resharper_place_type_constraints_on_same_line = true
resharper_prefer_explicit_discard_declaration = false
-resharper_prefer_separate_deconstructed_variables_declaration = true
+resharper_prefer_separate_deconstructed_variables_declaration = false
resharper_preserve_spaces_inside_tags = pre,textarea
resharper_qualified_using_at_nested_scope = false
resharper_quote_style = doublequoted
@@ -1069,6 +1063,7 @@ resharper_space_within_typeof_parentheses = false
resharper_space_within_type_argument_angles = false
resharper_space_within_type_parameter_angles = false
resharper_space_within_type_parameter_parentheses = false
+resharper_csharp_allow_alias = false
resharper_special_else_if_treatment = true
resharper_static_members_qualify_members = none
resharper_static_members_qualify_with = declared_type
@@ -1174,7 +1169,7 @@ resharper_arrange_default_value_when_type_evident_highlighting = suggestion
resharper_arrange_default_value_when_type_not_evident_highlighting = suggestion
resharper_arrange_local_function_body_highlighting = error
resharper_arrange_method_or_operator_body_highlighting = error
-resharper_arrange_missing_parentheses_highlighting = hint
+resharper_arrange_missing_parentheses_highlighting = none
resharper_arrange_namespace_body_highlighting = error
resharper_arrange_object_creation_when_type_evident_highlighting = suggestion
resharper_arrange_object_creation_when_type_not_evident_highlighting = suggestion
@@ -1289,7 +1284,7 @@ resharper_convert_to_using_declaration_highlighting = suggestion
resharper_convert_to_vb_auto_property_highlighting = suggestion
resharper_convert_to_vb_auto_property_when_possible_highlighting = hint
resharper_convert_to_vb_auto_property_with_private_setter_highlighting = hint
-resharper_convert_type_check_pattern_to_null_check_highlighting = warning
+resharper_convert_type_check_pattern_to_null_check_highlighting = none
resharper_convert_type_check_to_null_check_highlighting = warning
resharper_co_variant_array_conversion_highlighting = warning
resharper_c_declaration_with_implicit_int_type_highlighting = warning
@@ -1408,8 +1403,8 @@ resharper_meaningless_default_parameter_value_highlighting = warning
resharper_member_can_be_internal_highlighting = none
resharper_member_can_be_made_static_global_highlighting = hint
resharper_member_can_be_made_static_local_highlighting = hint
-resharper_member_can_be_private_global_highlighting = suggestion
-resharper_member_can_be_private_local_highlighting = suggestion
+resharper_member_can_be_private_global_highlighting = hint
+resharper_member_can_be_private_local_highlighting = hint
resharper_member_can_be_protected_global_highlighting = suggestion
resharper_member_can_be_protected_local_highlighting = suggestion
resharper_member_hides_interface_member_with_default_implementation_highlighting = warning
@@ -1537,7 +1532,7 @@ resharper_redundant_assignment_highlighting = warning
resharper_redundant_attribute_parentheses_highlighting = hint
resharper_redundant_attribute_usage_property_highlighting = suggestion
resharper_redundant_base_constructor_call_highlighting = warning
-resharper_redundant_base_qualifier_highlighting = warning
+resharper_redundant_base_qualifier_highlighting = none
resharper_redundant_blank_lines_highlighting = none
resharper_redundant_bool_compare_highlighting = warning
resharper_redundant_case_label_highlighting = warning
@@ -1547,7 +1542,7 @@ resharper_redundant_check_before_assignment_highlighting = warning
resharper_redundant_collection_initializer_element_braces_highlighting = hint
resharper_redundant_configure_await_highlighting = suggestion
resharper_redundant_declaration_semicolon_highlighting = hint
-resharper_redundant_default_member_initializer_highlighting = warning
+resharper_redundant_default_member_initializer_highlighting = hint
resharper_redundant_delegate_creation_highlighting = warning
resharper_redundant_disable_warning_comment_highlighting = warning
resharper_redundant_discard_designation_highlighting = suggestion
@@ -1872,4 +1867,18 @@ resharper_virtual_member_never_overridden_local_highlighting = suggestion
resharper_void_method_with_must_use_return_value_attribute_highlighting = warning
resharper_with_expression_instead_of_initializer_highlighting = suggestion
resharper_wrong_indent_size_highlighting = none
-resharper_xunit_xunit_test_with_console_output_highlighting = warning
\ No newline at end of file
+resharper_xunit_xunit_test_with_console_output_highlighting = warning
+resharper_arrange_constructor_or_destructor_body_highlighting = hint
+resharper_arrange_local_function_body_highlighting = hint
+resharper_arrange_method_or_operator_body_highlighting = hint
+resharper_arrange_null_checking_pattern_highlighting = suggestion
+resharper_enforce_do_while_statement_braces_highlighting = warning
+resharper_enforce_fixed_statement_braces_highlighting = warning
+resharper_enforce_foreach_statement_braces_highlighting = none
+resharper_enforce_for_statement_braces_highlighting = none
+resharper_enforce_if_statement_braces_highlighting = none
+resharper_enforce_lock_statement_braces_highlighting = warning
+resharper_enforce_while_statement_braces_highlighting = none
+resharper_remove_redundant_braces_highlighting = hint
+resharper_suggest_discard_declaration_var_style_highlighting = warning
+resharper_prefer_concrete_value_over_default_highlighting = hint
\ No newline at end of file
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 8cd28f9..0829392 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -22,7 +22,7 @@ jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
- if: "!contains(format('{0} {1}', github.event.head_commit.message, github.event.pull_request.title), '[ci-skip]')"
+ if: "github.event.pull_request.draft == false || github.event_name == 'push'"
permissions:
actions: read
contents: read
@@ -42,7 +42,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@v2
+ uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -53,12 +53,12 @@ jobs:
- name: Set up .NET
uses: actions/setup-dotnet@v4
with:
- dotnet-version: '8.x'
+ dotnet-version: '9.x'
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
- uses: github/codeql-action/autobuild@v2
+ uses: github/codeql-action/autobuild@v3
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml
index aecaa16..3aee085 100644
--- a/.github/workflows/dotnet.yml
+++ b/.github/workflows/dotnet.yml
@@ -10,7 +10,7 @@ jobs:
windows_build:
runs-on: windows-latest
- if: "!contains(format('{0} {1}', github.event.head_commit.message, github.event.pull_request.title), '[ci-skip]')"
+ if: "github.event.pull_request.draft == false || github.event_name == 'push'"
env:
DOTNET_NOLOGO: true
@@ -21,7 +21,9 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
- dotnet-version: 8.x
+ dotnet-version: |
+ 8.x
+ 9.x
- name: Restore dependencies
run: dotnet restore
- name: Build
@@ -32,7 +34,7 @@ jobs:
linux_build:
runs-on: ubuntu-latest
- if: "!contains(format('{0} {1}', github.event.head_commit.message, github.event.pull_request.title), '[ci-skip]')"
+ if: "github.event.pull_request.draft == false || github.event_name == 'push'"
env:
DOTNET_NOLOGO: true
@@ -43,7 +45,9 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
- dotnet-version: 8.x
+ dotnet-version: |
+ 8.x
+ 9.x
- name: Restore dependencies
run: dotnet restore
- name: Build
@@ -54,7 +58,7 @@ jobs:
osx_build:
runs-on: macos-latest
- if: "!contains(format('{0} {1}', github.event.head_commit.message, github.event.pull_request.title), '[ci-skip]')"
+ if: "github.event.pull_request.draft == false || github.event_name == 'push'"
env:
DOTNET_NOLOGO: true
@@ -65,7 +69,9 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
- dotnet-version: 8.x
+ dotnet-version: |
+ 8.x
+ 9.x
- name: Restore dependencies
run: dotnet restore
- name: Build
diff --git a/.github/workflows/publish_nightly.yml b/.github/workflows/publish_nightly.yml
index 7a30c16..63273e0 100644
--- a/.github/workflows/publish_nightly.yml
+++ b/.github/workflows/publish_nightly.yml
@@ -15,7 +15,9 @@ jobs:
- name: "Setup .NET"
uses: "actions/setup-dotnet@v4"
with:
- dotnet-version: 8.x
+ dotnet-version: |
+ 8.x
+ 9.x
- name: Build
run: dotnet build -c Release
diff --git a/.github/workflows/publish_release.yml b/.github/workflows/publish_release.yml
index a9f0855..77c0f18 100644
--- a/.github/workflows/publish_release.yml
+++ b/.github/workflows/publish_release.yml
@@ -21,7 +21,9 @@ jobs:
- name: "Setup .NET"
uses: "actions/setup-dotnet@v4"
with:
- dotnet-version: 8.x
+ dotnet-version: |
+ 8.x
+ 9.x
- name: Build
run: dotnet build -c Release
@@ -68,7 +70,9 @@ jobs:
- name: "Setup .NET"
uses: "actions/setup-dotnet@v4"
with:
- dotnet-version: 8.x
+ dotnet-version: |
+ 8.x
+ 9.x
- name: Build
run: dotnet build -c Release
@@ -116,7 +120,9 @@ jobs:
- name: "Setup .NET"
uses: "actions/setup-dotnet@v4"
with:
- dotnet-version: 8.x
+ dotnet-version: |
+ 8.x
+ 9.x
- name: Build
run: dotnet build -c Release
@@ -158,7 +164,9 @@ jobs:
- name: "Setup .NET"
uses: "actions/setup-dotnet@v2"
with:
- dotnet-version: 8.x
+ dotnet-version: |
+ 8.x
+ 9.x
- name: Build
run: dotnet build -c Release
diff --git a/Directory.Build.props b/Directory.Build.props
index 4a873dc..9f6a00b 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -2,7 +2,7 @@
- net8.0
+ net8.0;net9.0
latest
latest
enable
@@ -10,8 +10,8 @@
Nullable
True
True
- 1.1.12.0
- 1.1.12.0
+ 1.2.0.0
+ 1.2.0.0
Kotz
Copyright © Kotz 2023
https://github.com/Kaoticz/Json2Sharp
@@ -22,11 +22,11 @@
Apache-2.0
-
+
False
-
+
True
diff --git a/Json2SharpApp/Program.cs b/Json2SharpApp/Program.cs
index 41f8297..652deeb 100644
--- a/Json2SharpApp/Program.cs
+++ b/Json2SharpApp/Program.cs
@@ -14,7 +14,7 @@ internal sealed class Program
///
/// CLI arguments.
/// Exit code.
- private async static Task Main(string[] args)
+ private static async Task Main(string[] args)
{
var inputOption = new Option(["--input", "-i"], "The relative path to the JSON file in the file system.");
var outputOption = new Option(["--output", "-o"], "The relative path to the resulting file in the file system.");
diff --git a/Json2SharpLib/Common/TypeAliases.cs b/Json2SharpLib/Common/TypeAliases.cs
index 42ac895..1a1d104 100644
--- a/Json2SharpLib/Common/TypeAliases.cs
+++ b/Json2SharpLib/Common/TypeAliases.cs
@@ -27,6 +27,9 @@ internal static class TypeAliases
[typeof(object)] = "object",
[typeof(bool)] = "bool",
[typeof(char)] = "char",
+ [typeof(DateTime)] = "DateTime",
+ [typeof(DateTimeOffset)] = "DateTimeOffset",
+ [typeof(Guid)] = "Guid",
[typeof(string[])] = "string[]",
[typeof(int[])] = "int[]",
@@ -43,6 +46,9 @@ internal static class TypeAliases
[typeof(object[])] = "object[]",
[typeof(bool[])] = "bool[]",
[typeof(char[])] = "char[]",
+ [typeof(DateTime[])] = "DateTime[]",
+ [typeof(DateTimeOffset[])] = "DateTimeOffset[]",
+ [typeof(Guid[])] = "Guid[]",
[typeof(int?[])] = "int?[]",
[typeof(byte?[])] = "byte?[]",
@@ -57,6 +63,9 @@ internal static class TypeAliases
[typeof(decimal?[])] = "decimal?[]",
[typeof(bool?[])] = "bool?[]",
[typeof(char?[])] = "char?[]",
+ [typeof(DateTime?[])] = "DateTime?[]",
+ [typeof(DateTimeOffset?[])] = "DateTimeOffset?[]",
+ [typeof(Guid?[])] = "Guid?[]",
}.ToFrozenDictionary();
///
@@ -81,7 +90,7 @@ internal static class TypeAliases
[typeof(char)] = "str",
[typeof(DateTime)] = "datetime",
[typeof(DateTimeOffset)] = "datetime",
- [typeof(Guid)] = "uuid",
+ [typeof(Guid)] = "UUID",
[typeof(string[])] = "list[str]",
[typeof(int[])] = "list[int]",
@@ -100,7 +109,7 @@ internal static class TypeAliases
[typeof(char[])] = "list[str]",
[typeof(DateTime[])] = "list[datetime]",
[typeof(DateTimeOffset[])] = "list[datetime]",
- [typeof(Guid[])] = "list[uuid]",
+ [typeof(Guid[])] = "list[UUID]",
[typeof(int?[])] = "Optional[list[int]]",
[typeof(byte?[])] = "Optional[bytes]",
@@ -117,6 +126,6 @@ internal static class TypeAliases
[typeof(char?[])] = "Optional[list[str]]",
[typeof(DateTime?[])] = "Optional[list[datetime]]",
[typeof(DateTimeOffset?[])] = "Optional[list[datetime]]",
- [typeof(Guid?[])] = "Optional[list[uuid]]",
+ [typeof(Guid?[])] = "Optional[list[UUID]]",
}.ToFrozenDictionary();
}
\ No newline at end of file
diff --git a/Json2SharpLib/Emitters/Abstractions/CodeEmitter.cs b/Json2SharpLib/Emitters/Abstractions/CodeEmitter.cs
index 442e371..3c6b5ac 100644
--- a/Json2SharpLib/Emitters/Abstractions/CodeEmitter.cs
+++ b/Json2SharpLib/Emitters/Abstractions/CodeEmitter.cs
@@ -11,6 +11,17 @@ namespace Json2SharpLib.Emitters.Abstractions;
///
internal abstract class CodeEmitter : ICodeEmitter
{
+ private readonly Func _typeNameHandler;
+
+ ///
+ /// Initializes a language code emitter.
+ ///
+ ///
+ /// The handler that transforms the type name of each parsed property before it is used in generated class members.
+ ///
+ internal CodeEmitter(Func typeNameHandler)
+ => _typeNameHandler = typeNameHandler;
+
///
public abstract string Parse(string objectName, JsonElement jsonElement);
@@ -39,11 +50,11 @@ internal abstract class CodeEmitter : ICodeEmitter
/// The target language for the type name.
/// The type name.
/// Occurs when is not of type .
- protected static string GetObjectTypeName(ParsedJsonProperty property, Language language)
+ protected string GetObjectTypeName(ParsedJsonProperty property, Language language)
{
using var jsonEnumerator = property.JsonElement.EnumerateObject();
return (jsonEnumerator.Any())
- ? property.JsonName!.ToPascalCase()
+ ? _typeNameHandler(property.JsonName!)
: J2SUtils.GetAliasName(typeof(object), language);
}
@@ -57,7 +68,7 @@ protected static string GetObjectTypeName(ParsedJsonProperty property, Language
/// if the array contains elements, otherwise.
/// Occurs when is empty.
/// Occurs when is not of type .
- protected static bool IsArrayOfNullableType(ParsedJsonProperty property, Language language, IReadOnlyList childrenTypes, out string typeName)
+ protected bool IsArrayOfNullableType(ParsedJsonProperty property, Language language, IReadOnlyList childrenTypes, out string typeName)
{
if (childrenTypes.Count is 0)
throw new ArgumentException("Collection cannot be empty.", nameof(childrenTypes));
@@ -78,9 +89,9 @@ out var aliasName
IsCustomType: aliasName?.Equals(J2SUtils.GetAliasName(typeof(object), language), StringComparison.Ordinal)
) switch // Else
{
- (1, true, true) => className, // Array type is custom type
- (1, true, false) => aliasName, // Array type is non-object language type
- (1, false, _) => className, // Array type is custom type
+ (1, true, true) => _typeNameHandler(className), // Array type is custom type
+ (1, true, false) => aliasName, // Array type is non-object language type
+ (1, false, _) => _typeNameHandler(className), // Array type is custom type
_ => J2SUtils.GetAliasName(typeof(object), language), // Array type is object language type
};
diff --git a/Json2SharpLib/Emitters/CSharp/CSharpClassEmitter.cs b/Json2SharpLib/Emitters/CSharp/CSharpClassEmitter.cs
index 2359861..3ebfb84 100644
--- a/Json2SharpLib/Emitters/CSharp/CSharpClassEmitter.cs
+++ b/Json2SharpLib/Emitters/CSharp/CSharpClassEmitter.cs
@@ -27,7 +27,7 @@ internal sealed class CSharpClassEmitter : CodeEmitter
/// Parses JSON data into a C# struct, class, or record with the base body of a class.
///
/// The parsing options.
- internal CSharpClassEmitter(Json2SharpCSharpOptions options)
+ internal CSharpClassEmitter(Json2SharpCSharpOptions options) : base(options.TypeNameHandler)
{
_accessibility = options.AccessibilityLevel.ToCode() + options.TargetType switch
{
@@ -63,7 +63,6 @@ public override string Parse(string objectName, JsonElement jsonElement)
/// The C# struct, class, or record.
private string InternalParse(string objectName, JsonElement jsonElement, bool emitHeaders)
{
- objectName = objectName.ToPascalCase();
var properties = Json2Sharp.ParseProperties(jsonElement);
if (properties.Count is 0)
@@ -101,9 +100,11 @@ protected override string ParseCustomType(ParsedJsonProperty property)
if (!string.IsNullOrWhiteSpace(_serializationAttribute))
result += CreateMemberAttribute(_indentationPadding, _serializationAttribute, property.JsonName!) + Environment.NewLine;
+ var customType = GetObjectTypeName(property, Language.CSharp);
+
result += CreateMemberDeclaration(
_indentationPadding,
- GetObjectTypeName(property, Language.CSharp),
+ customType,
propertyName,
_setterType
) + Environment.NewLine;
@@ -116,14 +117,14 @@ protected override string ParseArrayType(ParsedJsonProperty property, IReadOnlyL
{
var propertyName = property.JsonName.ToPascalCase() ?? property.BclType.Name;
var result = string.Empty;
- var propertyType = (IsArrayOfNullableType(property, Language.CSharp, childrenTypes, out typeName))
- ? typeName + "?[]"
- : typeName + "[]";
+ var arraySuffix = (IsArrayOfNullableType(property, Language.CSharp, childrenTypes, out typeName))
+ ? "?[]"
+ : "[]";
if (!string.IsNullOrWhiteSpace(_serializationAttribute))
result += CreateMemberAttribute(_indentationPadding, _serializationAttribute, property.JsonName!) + Environment.NewLine;
- result += CreateMemberDeclaration(_indentationPadding, propertyType, propertyName, _setterType) + Environment.NewLine;
+ result += CreateMemberDeclaration(_indentationPadding, typeName + arraySuffix, propertyName, _setterType) + Environment.NewLine;
return result;
}
@@ -152,15 +153,19 @@ private StringBuilder BuildClassMembers(StringBuilder stringBuilder, List if custom types were added, otherwise.
private static bool AddCustomTypes(StringBuilder stringBuilder, List extraTypes)
{
- var sanitizedExtraTypes = extraTypes.Where(x => !string.IsNullOrWhiteSpace(x));
+ var sanitizedExtraTypes = extraTypes
+ .Where(x => !string.IsNullOrWhiteSpace(x))
+ .ToArray();
- if (!sanitizedExtraTypes.Any())
+ if (sanitizedExtraTypes.Length is 0)
return false;
stringBuilder.AppendLine(Environment.NewLine);
@@ -204,12 +211,16 @@ private bool HandleCustomType(ParsedJsonProperty property, StringBuilder stringB
switch (property.JsonElement.ValueKind)
{
case JsonValueKind.Object:
- var propertyName = property.JsonName ?? property.BclType.Name;
+ {
+ var propertyName = GetObjectTypeName(property, Language.CSharp);
+
extraTypes.Add(InternalParse(propertyName, property.JsonElement, false));
stringBuilder.AppendLine(ParseCustomType(property));
return true;
+ }
case JsonValueKind.Array:
+ {
var childrenTypes = J2SUtils.GetArrayTypes(property);
if (childrenTypes.Count is 0)
@@ -221,6 +232,7 @@ private bool HandleCustomType(ParsedJsonProperty property, StringBuilder stringB
extraTypes.Add(InternalParse(typeName, childrenTypes[0].JsonElement, false));
return true;
+ }
default:
return false;
}
diff --git a/Json2SharpLib/Emitters/CSharp/CSharpRecordEmitter.cs b/Json2SharpLib/Emitters/CSharp/CSharpRecordEmitter.cs
index e2f8b24..715e9fb 100644
--- a/Json2SharpLib/Emitters/CSharp/CSharpRecordEmitter.cs
+++ b/Json2SharpLib/Emitters/CSharp/CSharpRecordEmitter.cs
@@ -24,7 +24,7 @@ internal sealed class CSharpRecordEmitter : CodeEmitter
/// Creates an object that parses JSON data into a C# record using a primary constructor.
///
/// The parsing options.
- internal CSharpRecordEmitter(Json2SharpCSharpOptions options)
+ internal CSharpRecordEmitter(Json2SharpCSharpOptions options) : base(options.TypeNameHandler)
{
_accessibility = options.AccessibilityLevel.ToCode() + (options.IsSealed ? " sealed" : string.Empty);
_serializationAttributeType = options.SerializationAttribute;
@@ -50,7 +50,6 @@ public override string Parse(string objectName, JsonElement jsonElement)
/// The C# record.
private string InternalParse(string objectName, JsonElement jsonElement, bool emitHeaders)
{
- objectName = objectName.ToPascalCase();
var properties = Json2Sharp.ParseProperties(jsonElement);
if (properties.Count is 0)
@@ -81,12 +80,13 @@ private string InternalParse(string objectName, JsonElement jsonElement, bool em
protected override string ParseCustomType(ParsedJsonProperty property)
{
var propertyName = property.JsonName.ToPascalCase() ?? property.BclType.Name;
+ var typeName = GetObjectTypeName(property, Language.CSharp);
return CreateMemberDeclaration(
_indentationPadding,
_serializationAttribute,
property.JsonName!,
- GetObjectTypeName(property, Language.CSharp),
+ typeName,
propertyName
);
}
@@ -95,15 +95,15 @@ protected override string ParseCustomType(ParsedJsonProperty property)
protected override string ParseArrayType(ParsedJsonProperty property, IReadOnlyList childrenTypes, out string typeName)
{
var finalName = property.JsonName.ToPascalCase() ?? property.BclType.Name;
- var propertyType = (IsArrayOfNullableType(property, Language.CSharp, childrenTypes, out typeName))
- ? typeName + "?[]"
- : typeName + "[]";
+ var arraySuffix = (IsArrayOfNullableType(property, Language.CSharp, childrenTypes, out typeName))
+ ? "?[]"
+ : "[]";
return CreateMemberDeclaration(
_indentationPadding,
_serializationAttribute,
property.JsonName!,
- propertyType,
+ typeName + arraySuffix,
finalName
);
}
@@ -130,14 +130,18 @@ private StringBuilder BuildPrimaryCtorArguments(StringBuilder stringBuilder, Lis
? "?"
: string.Empty;
+ var propertyName = (property.JsonElement.ValueKind is not JsonValueKind.Array)
+ ? bclTypeName + nullableAnnotation
+ : string.IsNullOrWhiteSpace(nullableAnnotation)
+ ? bclTypeName
+ : bclTypeName.Insert(bclTypeName.Length - 2, nullableAnnotation);
+
stringBuilder.AppendLine(
CreateMemberDeclaration(
_indentationPadding,
_serializationAttribute,
property.JsonName!,
- (property.JsonElement.ValueKind is not JsonValueKind.Array)
- ? bclTypeName + nullableAnnotation
- : string.IsNullOrWhiteSpace(nullableAnnotation) ? bclTypeName : bclTypeName.Insert(bclTypeName.Length - 2, nullableAnnotation),
+ propertyName,
property.JsonName!.ToPascalCase()
)
);
@@ -156,9 +160,11 @@ private StringBuilder BuildPrimaryCtorArguments(StringBuilder stringBuilder, Lis
/// if custom types were added, otherwise.
private static bool AddCustomTypes(StringBuilder stringBuilder, List extraTypes)
{
- var sanitizedExtraTypes = extraTypes.Where(x => !string.IsNullOrWhiteSpace(x));
+ var sanitizedExtraTypes = extraTypes
+ .Where(x => !string.IsNullOrWhiteSpace(x))
+ .ToArray();
- if (!sanitizedExtraTypes.Any())
+ if (sanitizedExtraTypes.Length is 0)
return false;
stringBuilder.AppendLine(Environment.NewLine);
@@ -179,12 +185,16 @@ private bool HandleCustomType(ParsedJsonProperty property, StringBuilder stringB
switch (property.JsonElement.ValueKind)
{
case JsonValueKind.Object:
- var propertyName = property.JsonName ?? property.BclType.Name;
+ {
+ var propertyName = GetObjectTypeName(property, Language.CSharp);
extraTypes.Add(InternalParse(propertyName, property.JsonElement, false));
stringBuilder.AppendLine(ParseCustomType(property));
return true;
+ }
+
case JsonValueKind.Array:
+ {
var childrenTypes = J2SUtils.GetArrayTypes(property);
if (childrenTypes.Count is 0)
@@ -196,6 +206,7 @@ private bool HandleCustomType(ParsedJsonProperty property, StringBuilder stringB
extraTypes.Add(InternalParse(typeName, childrenTypes[0].JsonElement, false));
return true;
+ }
default:
return false;
}
diff --git a/Json2SharpLib/Emitters/Python/PythonClassEmitter.cs b/Json2SharpLib/Emitters/Python/PythonClassEmitter.cs
index c573aa3..1afcdf6 100644
--- a/Json2SharpLib/Emitters/Python/PythonClassEmitter.cs
+++ b/Json2SharpLib/Emitters/Python/PythonClassEmitter.cs
@@ -22,7 +22,7 @@ internal sealed class PythonClassEmitter : CodeEmitter
/// Creates an object that parses JSON data into a Python class.
///
/// The parsing options.
- internal PythonClassEmitter(Json2SharpPythonOptions options)
+ internal PythonClassEmitter(Json2SharpPythonOptions options) : base(options.TypeNameHandler)
{
_addTypeHint = options.AddTypeHints;
_indentationPadding = new string(
@@ -48,7 +48,6 @@ public override string Parse(string objectName, JsonElement jsonElement)
/// The Python class.
private string InternalParse(string objectName, JsonElement jsonElement, bool emitHeaders)
{
- objectName = objectName.ToPascalCase();
var properties = Json2Sharp.ParseProperties(jsonElement);
if (properties.Count is 0)
@@ -69,7 +68,7 @@ private string InternalParse(string objectName, JsonElement jsonElement, bool em
// Add the imports
if (emitHeaders && _addTypeHint)
{
- var hasUuid = stringBuilder.Contains(": uuid");
+ var hasUuid = stringBuilder.Contains(": UUID");
var hasDatetime = stringBuilder.Contains(": datetime");
var hasOptional = stringBuilder.Contains("Optional[");
@@ -77,7 +76,7 @@ private string InternalParse(string objectName, JsonElement jsonElement, bool em
stringBuilder.Insert(0, Environment.NewLine + Environment.NewLine);
if (hasUuid)
- stringBuilder.Insert(0, "import uuid" + Environment.NewLine);
+ stringBuilder.Insert(0, "from uuid import UUID" + Environment.NewLine);
if (hasDatetime)
stringBuilder.Insert(0, "from datetime import datetime" + Environment.NewLine);
@@ -92,11 +91,12 @@ private string InternalParse(string objectName, JsonElement jsonElement, bool em
///
protected override string ParseCustomType(ParsedJsonProperty property)
{
- var propertyType = (_addTypeHint)
- ? ": " + GetObjectTypeName(property, Language.Python)
- : string.Empty;
+ if (!_addTypeHint)
+ return $"{property.JsonName.ToSnakeCase()},";
- return $"{property.JsonName.ToSnakeCase()}{propertyType},";
+ var typeName = GetObjectTypeName(property, Language.Python);
+
+ return $"{property.JsonName.ToSnakeCase()}: {typeName},";
}
///
@@ -157,18 +157,18 @@ private static bool AddCustomTypes(StringBuilder stringBuilder, List ext
{
var sanitizedExtraTypes = extraTypes
.Where(x => !string.IsNullOrWhiteSpace(x))
- .Reverse();
+ .Reverse()
+ .ToArray();
+
+ if (sanitizedExtraTypes.Length is 0)
+ return false;
// Add the custom types at the top
- if (sanitizedExtraTypes.Any())
- {
- stringBuilder.Insert(0, Environment.NewLine + Environment.NewLine);
- stringBuilder.Insert(0, string.Join(Environment.NewLine + Environment.NewLine, sanitizedExtraTypes));
+ stringBuilder.Insert(0, Environment.NewLine + Environment.NewLine);
+ stringBuilder.Insert(0, string.Join(Environment.NewLine + Environment.NewLine, sanitizedExtraTypes));
- return true;
- }
+ return true;
- return false;
}
///
@@ -184,7 +184,7 @@ private bool HandleCustomType(ParsedJsonProperty property, StringBuilder stringB
{
case JsonValueKind.Object:
stringBuilder.AppendIndentedLine(ParseCustomType(property), _indentationPadding, 2);
- extraTypes.Add(InternalParse(property.JsonName!, property.JsonElement, false));
+ extraTypes.Add(InternalParse(GetObjectTypeName(property, Language.Python), property.JsonElement, false));
return true;
case JsonValueKind.Array:
diff --git a/Json2SharpLib/Emitters/Python/PythonDataClassEmitter.cs b/Json2SharpLib/Emitters/Python/PythonDataClassEmitter.cs
index 8420730..ec75ee6 100644
--- a/Json2SharpLib/Emitters/Python/PythonDataClassEmitter.cs
+++ b/Json2SharpLib/Emitters/Python/PythonDataClassEmitter.cs
@@ -21,7 +21,7 @@ internal sealed class PythonDataClassEmitter : CodeEmitter
/// Creates an object that parses JSON data into a Python data class.
///
/// The parsing options.
- internal PythonDataClassEmitter(Json2SharpPythonOptions options)
+ internal PythonDataClassEmitter(Json2SharpPythonOptions options) : base(options.TypeNameHandler)
{
_indentationPadding = new string(
options.IndentationPaddingCharacter is IndentationCharacterType.Space ? ' ' : '\t',
@@ -46,7 +46,6 @@ public override string Parse(string objectName, JsonElement jsonElement)
/// The Python data class.
private string InternalParse(string objectName, JsonElement jsonElement, bool emitHeaders)
{
- objectName = objectName.ToPascalCase();
var properties = Json2Sharp.ParseProperties(jsonElement);
if (properties.Count is 0)
@@ -62,8 +61,8 @@ private string InternalParse(string objectName, JsonElement jsonElement, bool em
{
stringBuilder.Insert(0, Environment.NewLine + Environment.NewLine);
- if (stringBuilder.Contains(": uuid"))
- stringBuilder.Insert(0, "import uuid" + Environment.NewLine);
+ if (stringBuilder.Contains(": UUID"))
+ stringBuilder.Insert(0, "from uuid import UUID" + Environment.NewLine);
if (stringBuilder.Contains(": datetime"))
stringBuilder.Insert(0, "from datetime import datetime" + Environment.NewLine);
@@ -79,7 +78,10 @@ private string InternalParse(string objectName, JsonElement jsonElement, bool em
///
protected override string ParseCustomType(ParsedJsonProperty property)
- => $"{property.JsonName.ToSnakeCase()}: {GetObjectTypeName(property, Language.Python)}";
+ {
+ var typeName = GetObjectTypeName(property, Language.Python);
+ return $"{property.JsonName.ToSnakeCase()}: {typeName}";
+ }
///
protected override string ParseArrayType(ParsedJsonProperty property, IReadOnlyList childrenTypes, out string typeName)
@@ -134,18 +136,18 @@ private static bool AddCustomTypes(StringBuilder stringBuilder, List ext
{
var sanitizedExtraTypes = extraTypes
.Where(x => !string.IsNullOrWhiteSpace(x))
- .Reverse();
+ .Reverse()
+ .ToArray();
// Add the custom types at the top
- if (sanitizedExtraTypes.Any())
- {
- stringBuilder.Insert(0, Environment.NewLine + Environment.NewLine);
- stringBuilder.Insert(0, string.Join(Environment.NewLine + Environment.NewLine, sanitizedExtraTypes));
+ if (sanitizedExtraTypes.Length is 0)
+ return false;
- return true;
- }
+ stringBuilder.Insert(0, Environment.NewLine + Environment.NewLine);
+ stringBuilder.Insert(0, string.Join(Environment.NewLine + Environment.NewLine, sanitizedExtraTypes));
+
+ return true;
- return false;
}
///
@@ -161,7 +163,7 @@ private bool HandleCustomType(ParsedJsonProperty property, StringBuilder stringB
{
case JsonValueKind.Object:
stringBuilder.AppendIndentedLine(ParseCustomType(property), _indentationPadding, 1);
- extraTypes.Add(InternalParse(property.JsonName!, property.JsonElement, false));
+ extraTypes.Add(InternalParse(GetObjectTypeName(property, Language.Python), property.JsonElement, false));
return true;
case JsonValueKind.Array:
diff --git a/Json2SharpLib/Json2SharpLib.csproj b/Json2SharpLib/Json2SharpLib.csproj
index bac2111..783a315 100644
--- a/Json2SharpLib/Json2SharpLib.csproj
+++ b/Json2SharpLib/Json2SharpLib.csproj
@@ -7,8 +7,8 @@
-
- compile;analysers
+
+ compile;analyzers
\ No newline at end of file
diff --git a/Json2SharpLib/Models/LanguageOptions/Abstractions/BaseLanguageOptions.cs b/Json2SharpLib/Models/LanguageOptions/Abstractions/BaseLanguageOptions.cs
index 9be9060..cf487f1 100644
--- a/Json2SharpLib/Models/LanguageOptions/Abstractions/BaseLanguageOptions.cs
+++ b/Json2SharpLib/Models/LanguageOptions/Abstractions/BaseLanguageOptions.cs
@@ -7,17 +7,25 @@ namespace Json2SharpLib.Models.LanguageOptions.Abstractions;
///
public abstract record BaseLanguageOptions
{
- private int _indentationCharacterAmount = 4;
+ private readonly int _indentationCharacterAmount = 4;
///
- /// Defines the character that should prepend member declarations.
+ /// Defines the character that should prepend member declarations.
/// Default is .
///
public IndentationCharacterType IndentationPaddingCharacter { get; init; } = IndentationCharacterType.Space;
///
- /// Defines the amount of that should
- /// be prepended to member declarations.
+ /// Defines a handler that transforms the type name of each parsed property before it is used in generated class
+ /// members. Use it to apply custom naming logic to the type name of nested objects.
+ /// Default is a function that returns the parsed property name in the language's default naming convention.
+ ///
+ /// The parsed property name.
+ public abstract Func TypeNameHandler { get; init; }
+
+ ///
+ /// Defines the amount of that should be prepended to member declarations.
+ ///
/// Default is 4.
///
public int IndentationCharacterAmount
diff --git a/Json2SharpLib/Models/LanguageOptions/Json2SharpCSharpOptions.cs b/Json2SharpLib/Models/LanguageOptions/Json2SharpCSharpOptions.cs
index c18fdfa..8c52d40 100644
--- a/Json2SharpLib/Models/LanguageOptions/Json2SharpCSharpOptions.cs
+++ b/Json2SharpLib/Models/LanguageOptions/Json2SharpCSharpOptions.cs
@@ -43,4 +43,7 @@ public sealed record Json2SharpCSharpOptions : BaseLanguageOptions
/// Default is .
///
public bool IsPropertyRequired { get; init; } = true;
+
+ ///
+ public override Func TypeNameHandler { get; init; } = static propertyName => propertyName.ToPascalCase();
}
\ No newline at end of file
diff --git a/Json2SharpLib/Models/LanguageOptions/Json2SharpPythonOptions.cs b/Json2SharpLib/Models/LanguageOptions/Json2SharpPythonOptions.cs
index 06a7b6b..f77f8e2 100644
--- a/Json2SharpLib/Models/LanguageOptions/Json2SharpPythonOptions.cs
+++ b/Json2SharpLib/Models/LanguageOptions/Json2SharpPythonOptions.cs
@@ -24,4 +24,7 @@ public sealed record Json2SharpPythonOptions : BaseLanguageOptions
/// Default is .
///
public bool UseOptional { get; init; }
+
+ ///
+ public override Func TypeNameHandler { get; init; } = static propertyName => propertyName.ToPascalCase();
}
\ No newline at end of file
diff --git a/Json2SharpTests/CSharpTests/CSharpDataTests.cs b/Json2SharpTests/CSharpTests/CSharpDataTests.cs
index 9c6b407..284812c 100644
--- a/Json2SharpTests/CSharpTests/CSharpDataTests.cs
+++ b/Json2SharpTests/CSharpTests/CSharpDataTests.cs
@@ -2,6 +2,7 @@
using Json2SharpLib.Enums;
using Json2SharpLib.Models;
using Json2SharpTests.CSharpTests.Models.Answers;
+using Kotz.Extensions;
namespace Json2SharpTests.CSharpTests;
@@ -80,6 +81,36 @@ internal void OutputTest(string className, string input, string expectedOutput,
actualOutput.Replace("\r", string.Empty)
);
}
+
+ [Theory]
+ [InlineData(nameof(CustomHandleTypes), CustomHandleTypes.Input, CustomHandleTypes.RecordPrimaryCtorOutputNoAtt, CSharpObjectType.Record, CSharpSerializationAttribute.NoAttribute)]
+ [InlineData(nameof(CustomHandleTypes), CustomHandleTypes.Input, CustomHandleTypes.RecordPrimaryCtorOutput, CSharpObjectType.Record, CSharpSerializationAttribute.NewtonsoftJson)]
+ [InlineData(nameof(CustomHandleTypes), CustomHandleTypes.Input, CustomHandleTypes.RecordOutput, CSharpObjectType.Record, CSharpSerializationAttribute.SystemTextJson)]
+ [InlineData(nameof(CustomHandleTypes), CustomHandleTypes.Input, CustomHandleTypes.ClassOutputNoAtt, CSharpObjectType.Class, CSharpSerializationAttribute.NoAttribute)]
+ [InlineData(nameof(CustomHandleTypes), CustomHandleTypes.Input, CustomHandleTypes.ClassOutput, CSharpObjectType.Class, CSharpSerializationAttribute.SystemTextJson)]
+ [InlineData(nameof(CustomHandleTypes), CustomHandleTypes.Input, CustomHandleTypes.StructOutput, CSharpObjectType.Struct, CSharpSerializationAttribute.SystemTextJson)]
+ internal void TypeHandleOutputTest(string className, string input, string expectedOutput, CSharpObjectType targetType, CSharpSerializationAttribute serializationAttribute)
+ {
+ var options = new Json2SharpOptions()
+ {
+ TargetLanguage = Language.CSharp,
+
+ CSharpOptions = new()
+ {
+ TargetType = targetType,
+ SerializationAttribute = serializationAttribute,
+ TypeNameHandler = propertyType => propertyType.ToSnakeCase()
+ }
+ };
+
+ var actualOutput = Json2Sharp.Parse(className.ToSnakeCase(), input, options);
+
+ Assert.Equal(
+ expectedOutput.Replace("\r", string.Empty),
+ actualOutput.Replace("\r", string.Empty)
+ );
+ }
+
[Theory]
[InlineData(ArrayRoot.BadInput1)]
diff --git a/Json2SharpTests/CSharpTests/Models/Answers/CustomHandleTypes.cs b/Json2SharpTests/CSharpTests/Models/Answers/CustomHandleTypes.cs
new file mode 100644
index 0000000..77d1e7e
--- /dev/null
+++ b/Json2SharpTests/CSharpTests/Models/Answers/CustomHandleTypes.cs
@@ -0,0 +1,193 @@
+namespace Json2SharpTests.CSharpTests.Models.Answers;
+
+internal static class CustomHandleTypes
+{
+ public const string Input = """
+ {
+ "null_thing": null,
+ "empty_thing": {},
+ "thing": [
+ {
+ "text": "hello world",
+ "number": 1,
+ "int_array": [ 1, 2, 3 ],
+ "prop_base:colon": 2,
+ "prop_custom:colon": { "blep": "nested" }
+ }
+ ]
+ }
+ """;
+
+ public const string RecordPrimaryCtorOutput = """
+ using Newtonsoft.Json;
+
+ public sealed record custom_handle_types(
+ [JsonProperty("null_thing")] object? NullThing,
+ [JsonProperty("empty_thing")] object EmptyThing,
+ [JsonProperty("thing")] thing[] Thing
+ );
+
+ public sealed record thing(
+ [JsonProperty("text")] string Text,
+ [JsonProperty("number")] int Number,
+ [JsonProperty("int_array")] int[] IntArray,
+ [JsonProperty("prop_base:colon")] int PropBaseColon,
+ [JsonProperty("prop_custom:colon")] prop_custom_colon PropCustomColon
+ );
+
+ public sealed record prop_custom_colon(
+ [JsonProperty("blep")] string Blep
+ );
+ """;
+
+ public const string RecordPrimaryCtorOutputNoAtt = """
+ public sealed record custom_handle_types(
+ object? NullThing,
+ object EmptyThing,
+ thing[] Thing
+ );
+
+ public sealed record thing(
+ string Text,
+ int Number,
+ int[] IntArray,
+ int PropBaseColon,
+ prop_custom_colon PropCustomColon
+ );
+
+ public sealed record prop_custom_colon(
+ string Blep
+ );
+ """;
+
+ public const string RecordOutput = """
+ using System.Text.Json.Serialization;
+
+ public sealed record custom_handle_types(
+ [property: JsonPropertyName("null_thing")] object? NullThing,
+ [property: JsonPropertyName("empty_thing")] object EmptyThing,
+ [property: JsonPropertyName("thing")] thing[] Thing
+ );
+
+ public sealed record thing(
+ [property: JsonPropertyName("text")] string Text,
+ [property: JsonPropertyName("number")] int Number,
+ [property: JsonPropertyName("int_array")] int[] IntArray,
+ [property: JsonPropertyName("prop_base:colon")] int PropBaseColon,
+ [property: JsonPropertyName("prop_custom:colon")] prop_custom_colon PropCustomColon
+ );
+
+ public sealed record prop_custom_colon(
+ [property: JsonPropertyName("blep")] string Blep
+ );
+ """;
+
+ public const string ClassOutput = """
+ using System.Text.Json.Serialization;
+
+ public sealed class custom_handle_types
+ {
+ [JsonPropertyName("null_thing")]
+ public required object? NullThing { get; init; }
+
+ [JsonPropertyName("empty_thing")]
+ public required object EmptyThing { get; init; }
+
+ [JsonPropertyName("thing")]
+ public required thing[] Thing { get; init; }
+ }
+
+ public sealed class thing
+ {
+ [JsonPropertyName("text")]
+ public required string Text { get; init; }
+
+ [JsonPropertyName("number")]
+ public required int Number { get; init; }
+
+ [JsonPropertyName("int_array")]
+ public required int[] IntArray { get; init; }
+
+ [JsonPropertyName("prop_base:colon")]
+ public required int PropBaseColon { get; init; }
+
+ [JsonPropertyName("prop_custom:colon")]
+ public required prop_custom_colon PropCustomColon { get; init; }
+ }
+
+ public sealed class prop_custom_colon
+ {
+ [JsonPropertyName("blep")]
+ public required string Blep { get; init; }
+ }
+ """;
+
+ public const string ClassOutputNoAtt = """
+ public sealed class custom_handle_types
+ {
+ public required object? NullThing { get; init; }
+
+ public required object EmptyThing { get; init; }
+
+ public required thing[] Thing { get; init; }
+ }
+
+ public sealed class thing
+ {
+ public required string Text { get; init; }
+
+ public required int Number { get; init; }
+
+ public required int[] IntArray { get; init; }
+
+ public required int PropBaseColon { get; init; }
+
+ public required prop_custom_colon PropCustomColon { get; init; }
+ }
+
+ public sealed class prop_custom_colon
+ {
+ public required string Blep { get; init; }
+ }
+ """;
+
+ public const string StructOutput = """
+ using System.Text.Json.Serialization;
+
+ public readonly struct custom_handle_types
+ {
+ [JsonPropertyName("null_thing")]
+ public required object? NullThing { get; init; }
+
+ [JsonPropertyName("empty_thing")]
+ public required object EmptyThing { get; init; }
+
+ [JsonPropertyName("thing")]
+ public required thing[] Thing { get; init; }
+ }
+
+ public readonly struct thing
+ {
+ [JsonPropertyName("text")]
+ public required string Text { get; init; }
+
+ [JsonPropertyName("number")]
+ public required int Number { get; init; }
+
+ [JsonPropertyName("int_array")]
+ public required int[] IntArray { get; init; }
+
+ [JsonPropertyName("prop_base:colon")]
+ public required int PropBaseColon { get; init; }
+
+ [JsonPropertyName("prop_custom:colon")]
+ public required prop_custom_colon PropCustomColon { get; init; }
+ }
+
+ public readonly struct prop_custom_colon
+ {
+ [JsonPropertyName("blep")]
+ public required string Blep { get; init; }
+ }
+ """;
+}
\ No newline at end of file
diff --git a/Json2SharpTests/Json2SharpTests.csproj b/Json2SharpTests/Json2SharpTests.csproj
index 518decc..c77bff5 100644
--- a/Json2SharpTests/Json2SharpTests.csproj
+++ b/Json2SharpTests/Json2SharpTests.csproj
@@ -1,6 +1,6 @@
- net8.0
+ net9.0
Unit tests
enable
enable
@@ -9,13 +9,14 @@
False
-
-
-
+
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/Json2SharpTests/PythonTests/Models/Answers/CustomHandleTypes.cs b/Json2SharpTests/PythonTests/Models/Answers/CustomHandleTypes.cs
new file mode 100644
index 0000000..a36c46d
--- /dev/null
+++ b/Json2SharpTests/PythonTests/Models/Answers/CustomHandleTypes.cs
@@ -0,0 +1,118 @@
+namespace Json2SharpTests.PythonTests.Models.Answers;
+
+internal static class CustomHandleTypes
+{
+ public const string Input = """
+ {
+ "null_thing": null,
+ "empty_thing": {},
+ "thing": {
+ "text": "hello world",
+ "number": 1,
+ "int_array": [ 1, 2, 3 ],
+ "prop_base:colon": 2,
+ "prop_custom:colon": { "blep": "nested" }
+ }
+ }
+ """;
+
+ public const string DataClassOutput = """
+ from dataclasses import dataclass
+ from typing import Optional
+
+
+ @dataclass
+ class prop_custom_colon:
+ blep: str
+
+
+ @dataclass
+ class thing:
+ text: str
+ number: int
+ int_array: list[int]
+ prop_base_colon: int
+ prop_custom_colon: prop_custom_colon
+
+
+ @dataclass
+ class custom_handle_types:
+ null_thing: Optional[object]
+ empty_thing: object
+ thing: thing
+
+ """;
+
+ public const string Output = """
+ from typing import Optional
+
+
+ class prop_custom_colon:
+ def __init__(
+ blep: str
+ ) -> None:
+ self.blep = blep
+
+
+ class thing:
+ def __init__(
+ text: str,
+ number: int,
+ int_array: list[int],
+ prop_base_colon: int,
+ prop_custom_colon: prop_custom_colon
+ ) -> None:
+ self.text = text
+ self.number = number
+ self.int_array = int_array
+ self.prop_base_colon = prop_base_colon
+ self.prop_custom_colon = prop_custom_colon
+
+
+ class custom_handle_types:
+ def __init__(
+ null_thing: Optional[object],
+ empty_thing: object,
+ thing: thing
+ ) -> None:
+ self.null_thing = null_thing
+ self.empty_thing = empty_thing
+ self.thing = thing
+
+ """;
+
+ public const string OutputNoTypeHints = """
+ class prop_custom_colon:
+ def __init__(
+ blep
+ ):
+ self.blep = blep
+
+
+ class thing:
+ def __init__(
+ text,
+ number,
+ int_array,
+ prop_base_colon,
+ prop_custom_colon
+ ):
+ self.text = text
+ self.number = number
+ self.int_array = int_array
+ self.prop_base_colon = prop_base_colon
+ self.prop_custom_colon = prop_custom_colon
+
+
+ class custom_handle_types:
+ def __init__(
+ null_thing,
+ empty_thing,
+ thing
+ ):
+ self.null_thing = null_thing
+ self.empty_thing = empty_thing
+ self.thing = thing
+
+ """;
+}
\ No newline at end of file
diff --git a/Json2SharpTests/PythonTests/Models/Answers/TextTypes.cs b/Json2SharpTests/PythonTests/Models/Answers/TextTypes.cs
index 1426f99..c47855c 100644
--- a/Json2SharpTests/PythonTests/Models/Answers/TextTypes.cs
+++ b/Json2SharpTests/PythonTests/Models/Answers/TextTypes.cs
@@ -14,7 +14,7 @@ internal static class TextTypes
public const string DataClassOutput = """
from dataclasses import dataclass
from datetime import datetime
- import uuid
+ from uuid import UUID
@dataclass
@@ -22,13 +22,13 @@ class TextTypes:
text: str
empty_text: str
date_time_offset: datetime
- id: uuid
+ id: UUID
""";
public const string Output = """
from datetime import datetime
- import uuid
+ from uuid import UUID
class TextTypes:
@@ -36,7 +36,7 @@ def __init__(
text: str,
empty_text: str,
date_time_offset: datetime,
- id: uuid
+ id: UUID
) -> None:
self.text = text
self.empty_text = empty_text
diff --git a/Json2SharpTests/PythonTests/PythonDataTests.cs b/Json2SharpTests/PythonTests/PythonDataTests.cs
index 778be21..237306e 100644
--- a/Json2SharpTests/PythonTests/PythonDataTests.cs
+++ b/Json2SharpTests/PythonTests/PythonDataTests.cs
@@ -2,6 +2,7 @@
using Json2SharpLib.Enums;
using Json2SharpLib.Models;
using Json2SharpTests.PythonTests.Models.Answers;
+using Kotz.Extensions;
namespace Json2SharpTests.PythonTests;
@@ -83,4 +84,32 @@ internal void OutputNoneTypeHintsTest(string className, string input, string exp
actualOutput.Replace("\r", string.Empty)
);
}
+
+
+ [Theory]
+ [InlineData(nameof(CustomHandleTypes), CustomHandleTypes.Input, CustomHandleTypes.Output, true, false)]
+ [InlineData(nameof(CustomHandleTypes), CustomHandleTypes.Input, CustomHandleTypes.DataClassOutput, true, true)]
+ [InlineData(nameof(CustomHandleTypes), CustomHandleTypes.Input, CustomHandleTypes.OutputNoTypeHints, false, false)]
+ internal void TypeHandleOutputTest(string className, string input, string expectedOutput, bool addTypeHints, bool useDataClass)
+ {
+ var options = new Json2SharpOptions()
+ {
+ TargetLanguage = Language.Python,
+
+ PythonOptions = new()
+ {
+ AddTypeHints = addTypeHints,
+ UseDataClass = useDataClass,
+ UseOptional = true,
+ TypeNameHandler = propertyType => propertyType.ToSnakeCase()
+ }
+ };
+
+ var actualOutput = Json2Sharp.Parse(className.ToSnakeCase(), input, options);
+
+ Assert.Equal(
+ expectedOutput.Replace("\r", string.Empty),
+ actualOutput.Replace("\r", string.Empty)
+ );
+ }
}
\ No newline at end of file