Converted to net6.0
This commit is contained in:
commit
2385affccb
244
.editorconfig
Normal file
244
.editorconfig
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
[*.cs]
|
||||||
|
csharp_indent_block_contents = true
|
||||||
|
csharp_indent_braces = false
|
||||||
|
csharp_indent_case_contents = true
|
||||||
|
csharp_indent_case_contents_when_block = true
|
||||||
|
csharp_indent_labels = one_less_than_current
|
||||||
|
csharp_indent_switch_labels = true
|
||||||
|
csharp_new_line_before_catch = true
|
||||||
|
csharp_new_line_before_else = true
|
||||||
|
csharp_new_line_before_finally = true
|
||||||
|
csharp_new_line_before_members_in_anonymous_types = true
|
||||||
|
csharp_new_line_before_members_in_object_initializers = true
|
||||||
|
csharp_new_line_before_open_brace = all
|
||||||
|
csharp_new_line_between_query_expression_clauses = true
|
||||||
|
csharp_prefer_braces = false
|
||||||
|
csharp_prefer_simple_default_expression = true:warning
|
||||||
|
csharp_prefer_simple_using_statement = true:warning
|
||||||
|
csharp_prefer_static_local_function = true:warning
|
||||||
|
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
|
||||||
|
csharp_preserve_single_line_blocks = true
|
||||||
|
csharp_preserve_single_line_statements = false
|
||||||
|
csharp_space_after_cast = false
|
||||||
|
csharp_space_after_colon_in_inheritance_clause = true
|
||||||
|
csharp_space_after_comma = true
|
||||||
|
csharp_space_after_dot = false
|
||||||
|
csharp_space_after_keywords_in_control_flow_statements = true
|
||||||
|
csharp_space_after_semicolon_in_for_statement = true
|
||||||
|
csharp_space_around_binary_operators = before_and_after
|
||||||
|
csharp_space_around_declaration_statements = false
|
||||||
|
csharp_space_before_colon_in_inheritance_clause = true
|
||||||
|
csharp_space_before_comma = false
|
||||||
|
csharp_space_before_dot = false
|
||||||
|
csharp_space_before_open_square_brackets = false
|
||||||
|
csharp_space_before_semicolon_in_for_statement = false
|
||||||
|
csharp_space_between_empty_square_brackets = false
|
||||||
|
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||||
|
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||||
|
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||||
|
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||||
|
csharp_space_between_method_declaration_name_and_open_parenthesis = false
|
||||||
|
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||||
|
csharp_space_between_parentheses = false
|
||||||
|
csharp_space_between_square_brackets = false
|
||||||
|
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
|
||||||
|
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
|
||||||
|
csharp_style_allow_embedded_statements_on_same_line_experimental = true
|
||||||
|
csharp_style_conditional_delegate_call = true
|
||||||
|
csharp_style_deconstructed_variable_declaration = false
|
||||||
|
csharp_style_expression_bodied_accessors = when_on_single_line:warning
|
||||||
|
csharp_style_expression_bodied_constructors = when_on_single_line:warning
|
||||||
|
csharp_style_expression_bodied_indexers = when_on_single_line:warning
|
||||||
|
csharp_style_expression_bodied_lambdas = when_on_single_line:warning
|
||||||
|
csharp_style_expression_bodied_local_functions = when_on_single_line:warning
|
||||||
|
csharp_style_expression_bodied_methods = when_on_single_line:warning
|
||||||
|
csharp_style_expression_bodied_operators = when_on_single_line:warning
|
||||||
|
csharp_style_expression_bodied_properties = when_on_single_line:warning
|
||||||
|
csharp_style_implicit_object_creation_when_type_is_apparent = true:warning
|
||||||
|
csharp_style_inlined_variable_declaration = false
|
||||||
|
csharp_style_namespace_declarations = file_scoped:warning
|
||||||
|
csharp_style_pattern_local_over_anonymous_function = true:warning
|
||||||
|
csharp_style_pattern_matching_over_as_with_null_check = true:warning
|
||||||
|
csharp_style_pattern_matching_over_is_with_cast_check = true:warning
|
||||||
|
csharp_style_prefer_index_operator = false:warning
|
||||||
|
csharp_style_prefer_not_pattern = true:warning
|
||||||
|
csharp_style_prefer_null_check_over_type_check = true
|
||||||
|
csharp_style_prefer_pattern_matching = true:warning
|
||||||
|
csharp_style_prefer_range_operator = false:warning
|
||||||
|
csharp_style_prefer_switch_expression = true:warning
|
||||||
|
csharp_style_throw_expression = true
|
||||||
|
csharp_style_unused_value_assignment_preference = discard_variable:warning
|
||||||
|
csharp_style_unused_value_expression_statement_preference = discard_variable:warning
|
||||||
|
csharp_style_var_elsewhere = false:warning
|
||||||
|
csharp_style_var_for_built_in_types = false:warning
|
||||||
|
csharp_style_var_when_type_is_apparent = false:warning
|
||||||
|
csharp_using_directive_placement = outside_namespace
|
||||||
|
dotnet_code_quality_unused_parameters = all
|
||||||
|
dotnet_code_quality_unused_parameters = non_public # IDE0060: Remove unused parameter
|
||||||
|
dotnet_code_quality.CAXXXX.api_surface = private, internal
|
||||||
|
dotnet_diagnostic.CA1825.severity = warning # CA1823: Avoid zero-length array allocations
|
||||||
|
dotnet_diagnostic.CA1829.severity = warning # CA1829: Use Length/Count property instead of Count() when available
|
||||||
|
dotnet_diagnostic.CA1834.severity = warning # CA1834: Consider using 'StringBuilder.Append(char)' when applicable
|
||||||
|
dotnet_diagnostic.IDE0001.severity = warning # IDE0001: Simplify name
|
||||||
|
dotnet_diagnostic.IDE0002.severity = warning # Simplify (member access) - System.Version.Equals("1", "2"); Version.Equals("1", "2");
|
||||||
|
dotnet_diagnostic.IDE0005.severity = warning # Using directive is unnecessary
|
||||||
|
dotnet_diagnostic.IDE0047.severity = warning # IDE0047: Parentheses can be removed
|
||||||
|
dotnet_diagnostic.IDE0060.severity = warning # IDE0060: Remove unused parameter
|
||||||
|
dotnet_naming_rule.abstract_method_should_be_pascal_case.severity = warning
|
||||||
|
dotnet_naming_rule.abstract_method_should_be_pascal_case.style = pascal_case
|
||||||
|
dotnet_naming_rule.abstract_method_should_be_pascal_case.symbols = abstract_method
|
||||||
|
dotnet_naming_rule.class_should_be_pascal_case.severity = warning
|
||||||
|
dotnet_naming_rule.class_should_be_pascal_case.style = pascal_case
|
||||||
|
dotnet_naming_rule.class_should_be_pascal_case.symbols = class
|
||||||
|
dotnet_naming_rule.delegate_should_be_pascal_case.severity = warning
|
||||||
|
dotnet_naming_rule.delegate_should_be_pascal_case.style = pascal_case
|
||||||
|
dotnet_naming_rule.delegate_should_be_pascal_case.symbols = delegate
|
||||||
|
dotnet_naming_rule.enum_should_be_pascal_case.severity = warning
|
||||||
|
dotnet_naming_rule.enum_should_be_pascal_case.style = pascal_case
|
||||||
|
dotnet_naming_rule.enum_should_be_pascal_case.symbols = enum
|
||||||
|
dotnet_naming_rule.event_should_be_pascal_case.severity = warning
|
||||||
|
dotnet_naming_rule.event_should_be_pascal_case.style = pascal_case
|
||||||
|
dotnet_naming_rule.event_should_be_pascal_case.symbols = event
|
||||||
|
dotnet_naming_rule.interface_should_be_begins_with_i.severity = warning
|
||||||
|
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
|
||||||
|
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
|
||||||
|
dotnet_naming_rule.method_should_be_pascal_case.severity = warning
|
||||||
|
dotnet_naming_rule.method_should_be_pascal_case.style = pascal_case
|
||||||
|
dotnet_naming_rule.method_should_be_pascal_case.symbols = method
|
||||||
|
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = warning
|
||||||
|
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
|
||||||
|
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
|
||||||
|
dotnet_naming_rule.private_method_should_be_pascal_case.severity = warning
|
||||||
|
dotnet_naming_rule.private_method_should_be_pascal_case.style = pascal_case
|
||||||
|
dotnet_naming_rule.private_method_should_be_pascal_case.symbols = private_method
|
||||||
|
dotnet_naming_rule.private_or_internal_field_should_be_private_of_internal_field.severity = warning
|
||||||
|
dotnet_naming_rule.private_or_internal_field_should_be_private_of_internal_field.style = private_of_internal_field
|
||||||
|
dotnet_naming_rule.private_or_internal_field_should_be_private_of_internal_field.symbols = private_or_internal_field
|
||||||
|
dotnet_naming_rule.private_or_internal_static_field_should_be_private_of_internal_field.severity = warning
|
||||||
|
dotnet_naming_rule.private_or_internal_static_field_should_be_private_of_internal_field.style = private_of_internal_field
|
||||||
|
dotnet_naming_rule.private_or_internal_static_field_should_be_private_of_internal_field.symbols = private_or_internal_static_field
|
||||||
|
dotnet_naming_rule.property_should_be_pascal_case.severity = warning
|
||||||
|
dotnet_naming_rule.property_should_be_pascal_case.style = pascal_case
|
||||||
|
dotnet_naming_rule.property_should_be_pascal_case.symbols = property
|
||||||
|
dotnet_naming_rule.public_or_protected_field_should_be_private_of_internal_field.severity = warning
|
||||||
|
dotnet_naming_rule.public_or_protected_field_should_be_private_of_internal_field.style = private_of_internal_field
|
||||||
|
dotnet_naming_rule.public_or_protected_field_should_be_private_of_internal_field.symbols = public_or_protected_field
|
||||||
|
dotnet_naming_rule.static_field_should_be_pascal_case.severity = warning
|
||||||
|
dotnet_naming_rule.static_field_should_be_pascal_case.style = pascal_case
|
||||||
|
dotnet_naming_rule.static_field_should_be_pascal_case.symbols = static_field
|
||||||
|
dotnet_naming_rule.static_method_should_be_pascal_case.severity = warning
|
||||||
|
dotnet_naming_rule.static_method_should_be_pascal_case.style = pascal_case
|
||||||
|
dotnet_naming_rule.static_method_should_be_pascal_case.symbols = static_method
|
||||||
|
dotnet_naming_rule.struct_should_be_pascal_case.severity = warning
|
||||||
|
dotnet_naming_rule.struct_should_be_pascal_case.style = pascal_case
|
||||||
|
dotnet_naming_rule.struct_should_be_pascal_case.symbols = struct
|
||||||
|
dotnet_naming_rule.types_should_be_pascal_case.severity = warning
|
||||||
|
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
|
||||||
|
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
|
||||||
|
dotnet_naming_style.begins_with_i.capitalization = pascal_case
|
||||||
|
dotnet_naming_style.begins_with_i.required_prefix = I
|
||||||
|
dotnet_naming_style.begins_with_i.required_suffix =
|
||||||
|
dotnet_naming_style.begins_with_i.word_separator =
|
||||||
|
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||||
|
dotnet_naming_style.pascal_case.required_prefix =
|
||||||
|
dotnet_naming_style.pascal_case.required_suffix =
|
||||||
|
dotnet_naming_style.pascal_case.word_separator =
|
||||||
|
dotnet_naming_style.private_of_internal_field.capitalization = pascal_case
|
||||||
|
dotnet_naming_style.private_of_internal_field.required_prefix = _
|
||||||
|
dotnet_naming_style.private_of_internal_field.required_suffix =
|
||||||
|
dotnet_naming_style.private_of_internal_field.word_separator =
|
||||||
|
dotnet_naming_symbols.abstract_method.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.abstract_method.applicable_kinds = method
|
||||||
|
dotnet_naming_symbols.abstract_method.required_modifiers = abstract
|
||||||
|
dotnet_naming_symbols.class.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.class.applicable_kinds = class
|
||||||
|
dotnet_naming_symbols.class.required_modifiers =
|
||||||
|
dotnet_naming_symbols.delegate.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.delegate.applicable_kinds = delegate
|
||||||
|
dotnet_naming_symbols.delegate.required_modifiers =
|
||||||
|
dotnet_naming_symbols.enum.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.enum.applicable_kinds = enum
|
||||||
|
dotnet_naming_symbols.enum.required_modifiers =
|
||||||
|
dotnet_naming_symbols.event.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.event.applicable_kinds = event
|
||||||
|
dotnet_naming_symbols.event.required_modifiers =
|
||||||
|
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||||
|
dotnet_naming_symbols.interface.required_modifiers =
|
||||||
|
dotnet_naming_symbols.method.applicable_accessibilities = public
|
||||||
|
dotnet_naming_symbols.method.applicable_kinds = method
|
||||||
|
dotnet_naming_symbols.method.required_modifiers =
|
||||||
|
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
|
||||||
|
dotnet_naming_symbols.non_field_members.required_modifiers =
|
||||||
|
dotnet_naming_symbols.private_method.applicable_accessibilities = private
|
||||||
|
dotnet_naming_symbols.private_method.applicable_kinds = method
|
||||||
|
dotnet_naming_symbols.private_method.required_modifiers =
|
||||||
|
dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected
|
||||||
|
dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field
|
||||||
|
dotnet_naming_symbols.private_or_internal_field.required_modifiers =
|
||||||
|
dotnet_naming_symbols.private_or_internal_static_field.applicable_accessibilities = internal, private, private_protected
|
||||||
|
dotnet_naming_symbols.private_or_internal_static_field.applicable_kinds = field
|
||||||
|
dotnet_naming_symbols.private_or_internal_static_field.required_modifiers = static
|
||||||
|
dotnet_naming_symbols.property.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.property.applicable_kinds = property
|
||||||
|
dotnet_naming_symbols.property.required_modifiers =
|
||||||
|
dotnet_naming_symbols.public_or_protected_field.applicable_accessibilities = public, protected
|
||||||
|
dotnet_naming_symbols.public_or_protected_field.applicable_kinds = field
|
||||||
|
dotnet_naming_symbols.public_or_protected_field.required_modifiers =
|
||||||
|
dotnet_naming_symbols.static_field.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.static_field.applicable_kinds = field
|
||||||
|
dotnet_naming_symbols.static_field.required_modifiers = static
|
||||||
|
dotnet_naming_symbols.static_method.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.static_method.applicable_kinds = method
|
||||||
|
dotnet_naming_symbols.static_method.required_modifiers = static
|
||||||
|
dotnet_naming_symbols.struct.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.struct.applicable_kinds = struct
|
||||||
|
dotnet_naming_symbols.struct.required_modifiers =
|
||||||
|
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
|
||||||
|
dotnet_naming_symbols.types.required_modifiers =
|
||||||
|
dotnet_remove_unnecessary_suppression_exclusions = 0
|
||||||
|
dotnet_separate_import_directive_groups = false
|
||||||
|
dotnet_sort_system_directives_first = false
|
||||||
|
dotnet_style_allow_multiple_blank_lines_experimental = false:warning
|
||||||
|
dotnet_style_allow_statement_immediately_after_block_experimental = true
|
||||||
|
dotnet_style_coalesce_expression = true
|
||||||
|
dotnet_style_collection_initializer = true:warning
|
||||||
|
dotnet_style_explicit_tuple_names = true:warning
|
||||||
|
dotnet_style_namespace_match_folder = true
|
||||||
|
dotnet_style_null_propagation = true:warning
|
||||||
|
dotnet_style_object_initializer = true:warning
|
||||||
|
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
||||||
|
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
|
||||||
|
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
|
||||||
|
dotnet_style_parentheses_in_other_operators = never_if_unnecessary
|
||||||
|
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
|
||||||
|
dotnet_style_predefined_type_for_locals_parameters_members = true
|
||||||
|
dotnet_style_predefined_type_for_member_access = true:warning
|
||||||
|
dotnet_style_prefer_auto_properties = true:warning
|
||||||
|
dotnet_style_prefer_compound_assignment = true:warning
|
||||||
|
dotnet_style_prefer_conditional_expression_over_assignment = false
|
||||||
|
dotnet_style_prefer_conditional_expression_over_return = false
|
||||||
|
dotnet_diagnostic.IDE0049.severity = warning
|
||||||
|
dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning
|
||||||
|
dotnet_style_prefer_inferred_tuple_names = true:warning
|
||||||
|
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning
|
||||||
|
dotnet_style_prefer_simplified_boolean_expressions = true:warning
|
||||||
|
dotnet_style_prefer_simplified_interpolation = true
|
||||||
|
dotnet_style_qualification_for_event = false:error
|
||||||
|
dotnet_style_qualification_for_field = false
|
||||||
|
dotnet_style_qualification_for_method = false:error
|
||||||
|
dotnet_style_qualification_for_property = false:error
|
||||||
|
dotnet_style_readonly_field = true:warning
|
||||||
|
dotnet_style_require_accessibility_modifiers = for_non_interface_members
|
||||||
|
end_of_line = crlf
|
||||||
|
file_header_template = unset
|
||||||
|
indent_size = 4
|
||||||
|
indent_style = space
|
||||||
|
insert_final_newline = false
|
||||||
|
root = true
|
||||||
|
tab_width = 4
|
||||||
|
# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1822
|
||||||
|
# https://github.com/dotnet/aspnetcore/blob/main/.editorconfig
|
||||||
|
# https://github.com/dotnet/project-system/blob/main/.editorconfig
|
266
.gitignore
vendored
Normal file
266
.gitignore
vendored
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
|
||||||
|
*.cache
|
||||||
|
*.testlog
|
||||||
|
.vs/Json2CSharpCodeGenerator/v16/.suo
|
||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
[Xx]64/
|
||||||
|
[Xx]86/
|
||||||
|
[Bb]uild/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
|
||||||
|
|
||||||
|
# Visual Studio 2015 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUNIT
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# DNX
|
||||||
|
project.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_i.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# JustCode is a .NET coding add-in
|
||||||
|
.JustCode
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
|
||||||
|
# TODO: Un-comment the next line if you do not want to checkin
|
||||||
|
# your web deploy settings because they may include unencrypted
|
||||||
|
# passwords
|
||||||
|
#*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/packages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/packages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignoreable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directory
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
[Ss]tyle[Cc]op.*
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
node_modules/
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# LightSwitch generated files
|
||||||
|
GeneratedArtifacts/
|
||||||
|
ModelManifest.xml
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
||||||
|
*.foldersync
|
||||||
|
*.sitecore
|
||||||
|
gulpfile.js
|
||||||
|
|
||||||
|
package-lock.json
|
||||||
|
|
||||||
|
package.json
|
||||||
|
|
||||||
|
#Exclude Serializations Folder
|
||||||
|
!./Serializations/**/
|
||||||
|
|
||||||
|
# Webpack
|
||||||
|
dist/
|
||||||
|
webpack.config.js
|
||||||
|
|
||||||
|
#OutputDir
|
||||||
|
/Publish
|
||||||
|
|
||||||
|
# IntelliJ-based IDE configuration (ie. Rider)
|
||||||
|
/.idea
|
1
.vscode/format-report.json
vendored
Normal file
1
.vscode/format-report.json
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
[]
|
26
.vscode/launch.json
vendored
Normal file
26
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
// Use IntelliSense to find out which attributes exist for C# debugging
|
||||||
|
// Use hover for the description of the existing attributes
|
||||||
|
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
|
||||||
|
"name": ".NET Core Launch (console)",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "build",
|
||||||
|
// If you have changed target frameworks, make sure to update the program path.
|
||||||
|
"program": "${workspaceFolder}/Json2CSharpCodeGenerator.WinForms/bin/Debug/net6.0-windows/win-x64/Json2CSharpCodeGenerator.WinForms.dll",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}/Json2CSharpCodeGenerator.WinForms",
|
||||||
|
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||||
|
"console": "internalConsole",
|
||||||
|
"stopAtEntry": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": ".NET Core Attach",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "attach"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
6
.vscode/settings.json
vendored
Normal file
6
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"cSpell.enabled": false,
|
||||||
|
"files.exclude": {
|
||||||
|
"**/.git": false
|
||||||
|
}
|
||||||
|
}
|
41
.vscode/tasks.json
vendored
Normal file
41
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "build",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"${workspaceFolder}/Json2CSharpCodeGenerator.WinForms/Json2CSharpCodeGenerator.WinForms.csproj",
|
||||||
|
"/property:GenerateFullPaths=true",
|
||||||
|
"/consoleloggerparameters:NoSummary"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "publish",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"publish",
|
||||||
|
"${workspaceFolder}/Json2CSharpCodeGenerator.WinForms/Json2CSharpCodeGenerator.WinForms.csproj",
|
||||||
|
"/property:GenerateFullPaths=true",
|
||||||
|
"/consoleloggerparameters:NoSummary"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "watch",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"watch",
|
||||||
|
"run",
|
||||||
|
"--project",
|
||||||
|
"${workspaceFolder}/Json2CSharpCodeGenerator.WinForms/Json2CSharpCodeGenerator.WinForms.csproj"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
513
Json2CSharpCodeGenerator.Lib/CodeWriters/CSharpCodeWriter.cs
Normal file
513
Json2CSharpCodeGenerator.Lib/CodeWriters/CSharpCodeWriter.cs
Normal file
@ -0,0 +1,513 @@
|
|||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Json2CSharpCodeGenerator.Lib.CodeWriters;
|
||||||
|
|
||||||
|
public class CSharpCodeWriter : ICodeBuilder
|
||||||
|
{
|
||||||
|
public string FileExtension => ".cs";
|
||||||
|
|
||||||
|
public string DisplayName => "C#";
|
||||||
|
|
||||||
|
private const string _NoRenameAttribute = "[Obfuscation(Feature = \"renaming\", Exclude = true)]";
|
||||||
|
private const string _NoPruneAttribute = "[Obfuscation(Feature = \"trigger\", Exclude = false)]";
|
||||||
|
|
||||||
|
private static readonly HashSet<string> _ReservedKeywords = new(comparer: StringComparer.Ordinal) {
|
||||||
|
"abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked", "class", "const", "continue",
|
||||||
|
"decimal", "default", "delegate", "do", "double", "else", "enum", "event", "explicit", "extern", "false", "finally",
|
||||||
|
"fixed", "float", "for", "foreach", "goto", "if", "implicit", "in", "int", "interface", "internal", "is", "lock", "long",
|
||||||
|
"namespace", "new", "null", "object", "operator", "out", "override", "params", "private", "protected", "public",
|
||||||
|
"readonly", "ref", "return", "sbyte", "sealed", "short", "sizeof", "stackalloc", "static", "string", "struct",
|
||||||
|
"switch", "this", "throw", "true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", "using",
|
||||||
|
"virtual", "void", "volatile", "while"
|
||||||
|
};
|
||||||
|
|
||||||
|
public bool IsReservedKeyword(string word) => _ReservedKeywords.Contains(word ?? string.Empty);
|
||||||
|
|
||||||
|
IReadOnlyCollection<string> ICodeBuilder.ReservedKeywords => _ReservedKeywords;
|
||||||
|
|
||||||
|
public string GetTypeName(JsonType type, IJsonClassGeneratorConfig config)
|
||||||
|
{
|
||||||
|
return type.Type switch
|
||||||
|
{
|
||||||
|
JsonTypeEnum.Anything => "object",
|
||||||
|
JsonTypeEnum.Array => GetCollectionTypeName(elementTypeName: GetTypeName(type.InternalType, config), config.CollectionType),
|
||||||
|
JsonTypeEnum.Dictionary => "Dictionary<string, " + GetTypeName(type.InternalType, config) + ">",
|
||||||
|
JsonTypeEnum.Boolean => "bool",
|
||||||
|
JsonTypeEnum.Float => "double",
|
||||||
|
JsonTypeEnum.Integer => "int",
|
||||||
|
JsonTypeEnum.Long => "long",
|
||||||
|
JsonTypeEnum.Date => "DateTime",
|
||||||
|
JsonTypeEnum.NonConstrained => "object",
|
||||||
|
JsonTypeEnum.NullableBoolean => "bool?",
|
||||||
|
JsonTypeEnum.NullableFloat => "double?",
|
||||||
|
JsonTypeEnum.NullableInteger => "int?",
|
||||||
|
JsonTypeEnum.NullableLong => "long?",
|
||||||
|
JsonTypeEnum.NullableDate => "DateTime?",
|
||||||
|
JsonTypeEnum.NullableSomething => "object",
|
||||||
|
JsonTypeEnum.Object => type.NewAssignedName,
|
||||||
|
JsonTypeEnum.String => "string",
|
||||||
|
_ => throw new NotSupportedException("Unsupported json type: " + type.Type),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetCollectionTypeName(string elementTypeName, OutputCollectionType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
OutputCollectionType.Array => elementTypeName + "[]",
|
||||||
|
OutputCollectionType.MutableList => "List<" + elementTypeName + ">",
|
||||||
|
OutputCollectionType.IReadOnlyList => "IReadOnlyList<" + elementTypeName + ">",
|
||||||
|
OutputCollectionType.ImmutableArray => "ImmutableArray<" + elementTypeName + ">",
|
||||||
|
_ => throw new ArgumentOutOfRangeException(paramName: nameof(type), actualValue: type, message: "Invalid " + nameof(OutputCollectionType) + " enum value."),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ShouldApplyNoRenamingAttribute(IJsonClassGeneratorConfig config) => config.ApplyObfuscationAttributes && !config.UsePascalCase;
|
||||||
|
|
||||||
|
private bool ShouldApplyNoPruneAttribute(IJsonClassGeneratorConfig config) => config.ApplyObfuscationAttributes && config.OutputType == OutputTypes.MutableClass && config.MutableClasses.Members == OutputMembers.AsPublicFields;
|
||||||
|
|
||||||
|
public void WriteFileStart(IJsonClassGeneratorConfig config, StringBuilder sw)
|
||||||
|
{
|
||||||
|
if (config.HasNamespace())
|
||||||
|
{
|
||||||
|
List<string> importNamespaces = new()
|
||||||
|
{
|
||||||
|
"System",
|
||||||
|
"System.Collections.Generic"
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ShouldApplyNoPruneAttribute(config) || ShouldApplyNoRenamingAttribute(config))
|
||||||
|
{
|
||||||
|
importNamespaces.Add("System.Reflection");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (config.AttributeLibrary)
|
||||||
|
{
|
||||||
|
case JsonLibrary.NewtonsoftJson:
|
||||||
|
importNamespaces.Add("Newtonsoft.Json");
|
||||||
|
importNamespaces.Add("Newtonsoft.Json.Linq");
|
||||||
|
break;
|
||||||
|
case JsonLibrary.SystemTextJson:
|
||||||
|
importNamespaces.Add("System.Text.Json");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(config.SecondaryNamespace) && !config.UseNestedClasses)
|
||||||
|
{
|
||||||
|
importNamespaces.Add(config.SecondaryNamespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
importNamespaces.Sort(CompareNamespacesSystemFirst);
|
||||||
|
|
||||||
|
foreach (string ns in importNamespaces) // NOTE: Using `.Distinct()` after sorting may cause out-of-order results.
|
||||||
|
{
|
||||||
|
_ = sw.AppendFormat("using {0};{1}", ns, Environment.NewLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.UseNestedClasses)
|
||||||
|
{
|
||||||
|
_ = sw.AppendFormat(" {0} class {1}", config.InternalVisibility ? "internal" : "public", config.MainClass);
|
||||||
|
_ = sw.AppendLine(" {");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int CompareNamespacesSystemFirst(string x, string y)
|
||||||
|
{
|
||||||
|
if (x == "System")
|
||||||
|
return -1;
|
||||||
|
if (y == "System")
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (x.StartsWith("System.", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
if (y.StartsWith("System.", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
// Both start with "System." - so compare them normally.
|
||||||
|
return StringComparer.Ordinal.Compare(x, y);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Only `x` starts with "System", so `x` should always come first (i.e. `x < y` or `y > x`).
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Only `y` starts with "System", so `y` should always come first (i.e. `x > y` or `y < x`).
|
||||||
|
if (y.StartsWith("System.", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Neither are "System." namespaces - so compare them normally.
|
||||||
|
return StringComparer.Ordinal.Compare(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteFileEnd(IJsonClassGeneratorConfig config, StringBuilder sw)
|
||||||
|
{
|
||||||
|
if (config.UseNestedClasses)
|
||||||
|
{
|
||||||
|
_ = sw.AppendLine(" }");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteDeserializationComment(IJsonClassGeneratorConfig config, StringBuilder sw, bool rootIsArray = false)
|
||||||
|
{
|
||||||
|
string deserializer;
|
||||||
|
switch (config.AttributeLibrary)
|
||||||
|
{
|
||||||
|
case JsonLibrary.NewtonsoftJson:
|
||||||
|
deserializer = "JsonConvert.DeserializeObject";
|
||||||
|
break;
|
||||||
|
case JsonLibrary.SystemTextJson:
|
||||||
|
deserializer = "JsonSerializer.Deserialize";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string rootType = rootIsArray ? "List<Root>" : "Root";
|
||||||
|
|
||||||
|
_ = sw.AppendLine($"// Root myDeserializedClass = {deserializer}<{rootType}>(myJsonResponse);");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteNamespaceStart(IJsonClassGeneratorConfig config, StringBuilder sw, bool root)
|
||||||
|
{
|
||||||
|
_ = sw.AppendLine();
|
||||||
|
_ = sw.AppendFormat("namespace {0}", root && !config.UseNestedClasses ? config.Namespace : (config.SecondaryNamespace ?? config.Namespace));
|
||||||
|
_ = sw.AppendLine("{");
|
||||||
|
_ = sw.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteNamespaceEnd(IJsonClassGeneratorConfig config, StringBuilder sw, bool root) => sw.AppendLine("}");
|
||||||
|
|
||||||
|
private static string GetTypeIndent(IJsonClassGeneratorConfig config, bool typeIsRoot)
|
||||||
|
{
|
||||||
|
if (config.UseNestedClasses)
|
||||||
|
{
|
||||||
|
if (typeIsRoot)
|
||||||
|
{
|
||||||
|
return " "; // 4x
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return " "; // 8x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return " "; // 4x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteClass(IJsonClassGeneratorConfig config, StringBuilder sw, JsonType type)
|
||||||
|
{
|
||||||
|
string indentTypes = GetTypeIndent(config, type.IsRoot);
|
||||||
|
string indentMembers = indentTypes + " ";
|
||||||
|
string indentBodies = indentMembers + " ";
|
||||||
|
|
||||||
|
const string visibility = "public";
|
||||||
|
|
||||||
|
string className = type.AssignedName;
|
||||||
|
if (config.OutputType == OutputTypes.ImmutableRecord)
|
||||||
|
{
|
||||||
|
sw.AppendFormat(indentTypes + "{0} record {1}({2}", visibility, className, Environment.NewLine);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sw.AppendFormat(indentTypes + "{0} class {1}{2}", visibility, className, Environment.NewLine);
|
||||||
|
sw.AppendLine(indentTypes + "{");
|
||||||
|
}
|
||||||
|
|
||||||
|
#if CAN_SUPRESS
|
||||||
|
var shouldSuppressWarning = config.InternalVisibility && !config.UseProperties && !config.ExplicitDeserialization;
|
||||||
|
if (shouldSuppressWarning)
|
||||||
|
{
|
||||||
|
sw.AppendFormat("#pragma warning disable 0649");
|
||||||
|
if (!config.UsePascalCase) sw.AppendLine();
|
||||||
|
}
|
||||||
|
if (config.ExplicitDeserialization)
|
||||||
|
{
|
||||||
|
if (config.UseProperties) WriteClassWithPropertiesExplicitDeserialization(sw, type, prefix);
|
||||||
|
else WriteClassWithFieldsExplicitDeserialization(sw, type, prefix);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
if (config.OutputType == OutputTypes.ImmutableClass)
|
||||||
|
{
|
||||||
|
WriteClassConstructor(config, sw, type, indentMembers: indentMembers, indentBodies: indentBodies);
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteClassMembers(config, sw, type, indentMembers);
|
||||||
|
}
|
||||||
|
#if CAN_SUPPRESS
|
||||||
|
if (shouldSuppressWarning)
|
||||||
|
{
|
||||||
|
sw.WriteLine();
|
||||||
|
sw.WriteLine("#pragma warning restore 0649");
|
||||||
|
sw.WriteLine();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (config.OutputType == OutputTypes.ImmutableRecord)
|
||||||
|
{
|
||||||
|
sw.AppendLine(indentTypes + ");");
|
||||||
|
}
|
||||||
|
else if ((!config.UseNestedClasses) || (config.UseNestedClasses && !type.IsRoot))
|
||||||
|
{
|
||||||
|
sw.AppendLine(indentTypes + "}");
|
||||||
|
}
|
||||||
|
|
||||||
|
sw.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Converts an identifier from JSON into a C#-safe PascalCase identifier.</summary>
|
||||||
|
private string GetCSharpPascalCaseName(string name)
|
||||||
|
{
|
||||||
|
// Check if property is a reserved keyword
|
||||||
|
if (IsReservedKeyword(name))
|
||||||
|
name = "@" + name;
|
||||||
|
|
||||||
|
// Check if property name starts with number
|
||||||
|
if (!string.IsNullOrEmpty(name) && char.IsDigit(name[0]))
|
||||||
|
name = "_" + name;
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Converts a camelCase identifier from JSON into a C#-safe camelCase identifier.</summary>
|
||||||
|
private string GetCSharpCamelCaseName(string camelCaseFromJson)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(camelCaseFromJson))
|
||||||
|
throw new ArgumentException(message: "Value cannot be null or empty.", paramName: nameof(camelCaseFromJson));
|
||||||
|
|
||||||
|
string name = camelCaseFromJson;
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
if (name.Length >= 3)
|
||||||
|
{
|
||||||
|
if (char.IsUpper(name[0]) && char.IsUpper(name[1]) && char.IsLower(name[2]))
|
||||||
|
{
|
||||||
|
// "ABc" --> "abc" // this may be wrong in some cases, if the first two letters are a 2-letter acronym, like "IO".
|
||||||
|
name = name.Substring(startIndex: 0, length: 2).ToLowerInvariant() + name.Substring(startIndex: 2);
|
||||||
|
}
|
||||||
|
else if (char.IsUpper(name[0]))
|
||||||
|
{
|
||||||
|
// "Abc" --> "abc"
|
||||||
|
// "AbC" --> "abC"
|
||||||
|
name = char.ToLower(name[0]) + name.Substring(startIndex: 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (name.Length == 2)
|
||||||
|
{
|
||||||
|
if (char.IsUpper(name[0]))
|
||||||
|
{
|
||||||
|
// "AB" --> "ab"
|
||||||
|
// "Ab" --> "ab"
|
||||||
|
name = name.ToLowerInvariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // Implicit: name.Length == 1
|
||||||
|
{
|
||||||
|
// "A" --> "a"
|
||||||
|
name = name.ToLowerInvariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!char.IsLetter(name[0]))
|
||||||
|
name = "_" + name;
|
||||||
|
else if (IsReservedKeyword(name))
|
||||||
|
name = "@" + name;
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteClassMembers(IJsonClassGeneratorConfig config, StringBuilder sw, JsonType type, string indentMembers)
|
||||||
|
{
|
||||||
|
bool first = true;
|
||||||
|
foreach (FieldInfo field in type.Fields)
|
||||||
|
{
|
||||||
|
string classPropertyName = GetCSharpPascalCaseName(field.MemberName);
|
||||||
|
string propertyAttribute = config.GetCSharpJsonAttributeCode(field);
|
||||||
|
|
||||||
|
// If we are using record types and this is not the first iteration, add a comma and newline to the previous line
|
||||||
|
// this is required because every line except the last should end with a comma
|
||||||
|
if (config.OutputType == OutputTypes.ImmutableRecord && !first)
|
||||||
|
{
|
||||||
|
_ = sw.AppendLine(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!first && ((propertyAttribute.Length > 0 && config.OutputType != OutputTypes.ImmutableRecord) || config.ExamplesInDocumentation))
|
||||||
|
{
|
||||||
|
// If rendering examples/XML comments - or property attributes - then add a newline before the property for readability's sake (except if it's the first property in the class)
|
||||||
|
// For record types, we want all members to be next to each other, unless when using examples
|
||||||
|
_ = sw.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.ExamplesInDocumentation)
|
||||||
|
{
|
||||||
|
_ = sw.AppendFormat(indentMembers + "/// <summary>");
|
||||||
|
_ = sw.AppendFormat(indentMembers + "/// Examples: " + field.GetExamplesText());
|
||||||
|
_ = sw.AppendFormat(indentMembers + "/// </summary>");
|
||||||
|
_ = sw.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (propertyAttribute.Length > 0)
|
||||||
|
{
|
||||||
|
_ = sw.Append(indentMembers);
|
||||||
|
_ = sw.Append(propertyAttribute);
|
||||||
|
|
||||||
|
if (config.OutputType != OutputTypes.ImmutableRecord)
|
||||||
|
{
|
||||||
|
_ = sw.AppendLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// record types is not compatible with UseFields, so it comes first
|
||||||
|
if (config.OutputType == OutputTypes.ImmutableRecord)
|
||||||
|
{
|
||||||
|
// NOTE: not adding newlines here, that happens at the start of the loop. We need this so we can lazily add commas at the end.
|
||||||
|
if (field.Type.Type == JsonTypeEnum.Array)
|
||||||
|
{
|
||||||
|
// TODO: Respect config.CollectionType
|
||||||
|
_ = sw.AppendFormat(" IReadOnlyList<{0}> {1}", GetTypeName(field.Type.InternalType, config), classPropertyName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_ = sw.AppendFormat(" {0} {1}", field.Type.GetTypeName(), classPropertyName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (config.OutputType == OutputTypes.MutableClass)
|
||||||
|
{
|
||||||
|
if (config.MutableClasses.Members == OutputMembers.AsPublicFields)
|
||||||
|
{
|
||||||
|
// Render a field like `public int Foobar;`:
|
||||||
|
|
||||||
|
bool useReadonlyModifier = config.OutputType == OutputTypes.ImmutableClass;
|
||||||
|
|
||||||
|
_ = sw.AppendFormat(indentMembers + "public {0}{1} {2};{3}", useReadonlyModifier ? "readonly " : "", field.Type.GetTypeName(), classPropertyName, Environment.NewLine);
|
||||||
|
}
|
||||||
|
else if (config.MutableClasses.Members == OutputMembers.AsProperties)
|
||||||
|
{
|
||||||
|
string getterSetterPart = "{ get; set; }";
|
||||||
|
|
||||||
|
bool addCollectionPropertyInitializer =
|
||||||
|
config.MutableClasses.ReadOnlyCollectionProperties &&
|
||||||
|
field.Type.IsCollectionType() &&
|
||||||
|
config.CollectionType == OutputCollectionType.MutableList;
|
||||||
|
|
||||||
|
if (addCollectionPropertyInitializer && field.Type.Type == JsonTypeEnum.Array)
|
||||||
|
{
|
||||||
|
getterSetterPart = "{ get; } = new " + field.Type.GetTypeName() + "();";
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = sw.AppendFormat(indentMembers + "public {0} {1} {2}{3}", field.Type.GetTypeName(), classPropertyName, getterSetterPart, Environment.NewLine);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const string PATH = nameof(config) + "." + nameof(config.MutableClasses) + "." + nameof(config.MutableClasses.Members);
|
||||||
|
const string MSG_FMT = "Invalid " + nameof(OutputMembers) + " enum value for " + PATH + ": {0}";
|
||||||
|
throw new InvalidOperationException(MSG_FMT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (config.OutputType == OutputTypes.ImmutableClass)
|
||||||
|
{
|
||||||
|
if (field.Type.Type == JsonTypeEnum.Array)
|
||||||
|
{
|
||||||
|
// TODO: Respect config.CollectionType
|
||||||
|
_ = sw.AppendFormat(indentMembers + "public IReadOnlyList<{0}> {1} {{ get; }}{2}", GetTypeName(field.Type.InternalType, config), classPropertyName, Environment.NewLine);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_ = sw.AppendFormat(indentMembers + "public {0} {1} {{ get; }}{2}", field.Type.GetTypeName(), classPropertyName, Environment.NewLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Invalid " + nameof(OutputTypes) + " value: " + config.OutputType);
|
||||||
|
}
|
||||||
|
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// emit a final newline if we're dealing with record types
|
||||||
|
if (config.OutputType == OutputTypes.ImmutableRecord)
|
||||||
|
{
|
||||||
|
_ = sw.AppendLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteClassConstructor(IJsonClassGeneratorConfig config, StringBuilder sw, JsonType type, string indentMembers, string indentBodies)
|
||||||
|
{
|
||||||
|
// Write an empty constructor on a single-line:
|
||||||
|
if (type.Fields.Count == 0)
|
||||||
|
{
|
||||||
|
_ = sw.AppendFormat(indentMembers + "public {0}() {{}}{1}", type.AssignedName, Environment.NewLine);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructor signature:
|
||||||
|
{
|
||||||
|
switch (config.AttributeLibrary)
|
||||||
|
{
|
||||||
|
case JsonLibrary.NewtonsoftJson:
|
||||||
|
case JsonLibrary.SystemTextJson: // Both libraries use the same attribute name: [JsonConstructor]
|
||||||
|
_ = sw.AppendLine(indentMembers + "[JsonConstructor]");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = sw.AppendFormat(indentMembers + "public {0}({1}", type.AssignedName, Environment.NewLine);
|
||||||
|
|
||||||
|
FieldInfo lastField = type.Fields[type.Fields.Count - 1];
|
||||||
|
|
||||||
|
foreach (FieldInfo field in type.Fields)
|
||||||
|
{
|
||||||
|
// Writes something like: `[JsonProperty("foobar")] string foobar,`
|
||||||
|
|
||||||
|
string ctorParameterName = GetCSharpCamelCaseName(field.MemberName);
|
||||||
|
|
||||||
|
bool isLast = ReferenceEquals(field, lastField);
|
||||||
|
string comma = isLast ? "" : ",";
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
_ = sw.Append(indentBodies);
|
||||||
|
|
||||||
|
string attribute = config.GetCSharpJsonAttributeCode(field);
|
||||||
|
if (attribute.Length > 0)
|
||||||
|
{
|
||||||
|
_ = sw.Append(attribute);
|
||||||
|
_ = sw.Append(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
// e.g. `String foobar,\r\n`
|
||||||
|
_ = sw.AppendFormat("{0} {1}{2}{3}", /*0:*/ field.Type.GetTypeName(), /*1:*/ ctorParameterName, /*2:*/ comma, /*3:*/ Environment.NewLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = sw.AppendLine(indentMembers + ")");
|
||||||
|
|
||||||
|
// Constructor body:
|
||||||
|
_ = sw.AppendLine(indentMembers + "{");
|
||||||
|
|
||||||
|
foreach (FieldInfo field in type.Fields)
|
||||||
|
{
|
||||||
|
string ctorParameterName = GetCSharpCamelCaseName(field.MemberName);
|
||||||
|
string classPropertyName = GetCSharpPascalCaseName(field.MemberName);
|
||||||
|
|
||||||
|
if (config.MutableClasses.ReadOnlyCollectionProperties || !string.IsNullOrEmpty(classPropertyName))
|
||||||
|
_ = sw.AppendFormat(indentBodies + "{0} = {1};{2}", /*0:*/ classPropertyName, /*1:*/ ctorParameterName, /*2:*/ Environment.NewLine);
|
||||||
|
else
|
||||||
|
_ = sw.AppendFormat(indentBodies + "this.{0} = {1};{2}", /*0:*/ classPropertyName, /*1:*/ ctorParameterName, /*2:*/ Environment.NewLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = sw.AppendLine(indentMembers + "}");
|
||||||
|
_ = sw.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
209
Json2CSharpCodeGenerator.Lib/CodeWriters/DartCodeWriter.cs
Normal file
209
Json2CSharpCodeGenerator.Lib/CodeWriters/DartCodeWriter.cs
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Json2CSharpCodeGenerator.Lib.CodeWriters;
|
||||||
|
|
||||||
|
public class DartCodeWriter : ICodeBuilder
|
||||||
|
{
|
||||||
|
public string FileExtension => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public string DisplayName => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public IReadOnlyCollection<string> ReservedKeywords => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public string GetTypeName(JsonType type, IJsonClassGeneratorConfig config)
|
||||||
|
{
|
||||||
|
return type.Type switch
|
||||||
|
{
|
||||||
|
//case JsonTypeEnum.Anything: return "object"; // Later, test to do
|
||||||
|
JsonTypeEnum.Array => GetCollectionTypeName(elementTypeName: GetTypeName(type.InternalType, config), config.CollectionType),
|
||||||
|
// case JsonTypeEnum.Dictionary: return "Dictionary<string, " + this.GetTypeName(type.InternalType, config) + ">";
|
||||||
|
JsonTypeEnum.Boolean => "bool?",
|
||||||
|
JsonTypeEnum.Float => "double?",
|
||||||
|
JsonTypeEnum.Integer => "int?",
|
||||||
|
JsonTypeEnum.Long => "double?",
|
||||||
|
JsonTypeEnum.Date => "DateTime?",
|
||||||
|
//case JsonTypeEnum.NonConstrained: return "object"; // Later, test to do
|
||||||
|
//case JsonTypeEnum.NullableBoolean: return "bool?";
|
||||||
|
//case JsonTypeEnum.NullableFloat: return "double?";
|
||||||
|
//case JsonTypeEnum.NullableInteger: return "int?";
|
||||||
|
//case JsonTypeEnum.NullableLong: return "long?";
|
||||||
|
//case JsonTypeEnum.NullableDate: return "DateTime?";
|
||||||
|
//case JsonTypeEnum.NullableSomething: return "object";
|
||||||
|
JsonTypeEnum.Object => type.NewAssignedName + "?",
|
||||||
|
JsonTypeEnum.String => "String?",
|
||||||
|
_ => throw new NotSupportedException("Unsupported json type: " + type.Type),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetTypeFunctionName(JsonType type, IJsonClassGeneratorConfig config)
|
||||||
|
{
|
||||||
|
return type.Type switch
|
||||||
|
{
|
||||||
|
JsonTypeEnum.Anything => "",
|
||||||
|
JsonTypeEnum.Array => "[" + type.InternalType.NewAssignedName + ".from_dict(y) for y in {0}]",
|
||||||
|
// case JsonTypeEnum.Dictionary: return "Dictionary<string, " + this.GetTypeName(type.InternalType, config) + ">";
|
||||||
|
JsonTypeEnum.Boolean => "",
|
||||||
|
JsonTypeEnum.Float => "float({0})",
|
||||||
|
JsonTypeEnum.Integer => "int({0})",
|
||||||
|
JsonTypeEnum.Long => "float({0})",
|
||||||
|
//case JsonTypeEnum.Date: return "DateTime"; // Do Later
|
||||||
|
JsonTypeEnum.Date => "str({0})",
|
||||||
|
JsonTypeEnum.NonConstrained => "",
|
||||||
|
// case JsonTypeEnum.NullableBoolean: return "bool?";
|
||||||
|
//case JsonTypeEnum.NullableFloat: return "double?";
|
||||||
|
//case JsonTypeEnum.NullableInteger: return "int?";
|
||||||
|
//case JsonTypeEnum.NullableLong: return "long?";
|
||||||
|
//case JsonTypeEnum.NullableDate: return "DateTime?";
|
||||||
|
//case JsonTypeEnum.NullableSomething: return "object";
|
||||||
|
JsonTypeEnum.Object => type.NewAssignedName + ".from_dict({0})",
|
||||||
|
JsonTypeEnum.String => "str({0})",
|
||||||
|
_ => throw new NotSupportedException("Unsupported json type: " + type.Type),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsReservedKeyword(string word) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public void WriteClass(IJsonClassGeneratorConfig config, StringBuilder sw, JsonType type)
|
||||||
|
{
|
||||||
|
string className = type.AssignedName;
|
||||||
|
|
||||||
|
_ = sw.AppendFormat("class {0} {{{1}", className, Environment.NewLine);
|
||||||
|
WriteClassMembers(config, sw, type, "");
|
||||||
|
_ = sw.AppendLine("}");
|
||||||
|
_ = sw.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteClassMembers(IJsonClassGeneratorConfig config, StringBuilder sw, JsonType type, string prefix)
|
||||||
|
{
|
||||||
|
StringBuilder fields = new();
|
||||||
|
StringBuilder fromJsonMapping = new();
|
||||||
|
StringBuilder toJsonMapping = new();
|
||||||
|
|
||||||
|
if (type.Fields.Any())
|
||||||
|
{
|
||||||
|
_ = fields.Append('{');
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (FieldInfo field in type.Fields)
|
||||||
|
{
|
||||||
|
_ = field.MemberName;
|
||||||
|
string propertyAttribute = field.JsonMemberName.RemoveSpecialCharacters().ToCamelCase();
|
||||||
|
string originalPropertyAttribute = field.JsonMemberName;
|
||||||
|
|
||||||
|
_ = sw.AppendFormat(" {0} {1};{2}", field.Type.GetTypeName(), propertyAttribute, Environment.NewLine);
|
||||||
|
|
||||||
|
_ = fields.Append("this." + propertyAttribute + ", ");
|
||||||
|
if (field.Type.Type == JsonTypeEnum.Array)
|
||||||
|
{
|
||||||
|
_ = fromJsonMapping.AppendLine(string.Format(" if (json['{0}'] != null) {{", originalPropertyAttribute));
|
||||||
|
_ = fromJsonMapping.AppendLine(string.Format(" {0} = <{1}>[];", propertyAttribute, field.Type.InternalType.NewAssignedName));
|
||||||
|
_ = fromJsonMapping.AppendLine(string.Format(" json['{0}'].forEach((v) {{", originalPropertyAttribute));
|
||||||
|
_ = fromJsonMapping.AppendLine(string.Format(" {0}!.add({1}.fromJson(v));", propertyAttribute, field.Type.InternalType.NewAssignedName));
|
||||||
|
_ = fromJsonMapping.AppendLine(" });");
|
||||||
|
_ = fromJsonMapping.AppendLine(" }");
|
||||||
|
|
||||||
|
_ = toJsonMapping.AppendLine(string.Format(" data['{0}'] ={0} != null ? {1}!.map((v) => v?.toJson()).toList() : null;", originalPropertyAttribute, propertyAttribute));
|
||||||
|
}
|
||||||
|
else if (field.Type.Type == JsonTypeEnum.Object)
|
||||||
|
{
|
||||||
|
_ = fromJsonMapping.AppendLine(string.Format(" {1} = json['{0}'] != null ? {2}.fromJson(json['{0}']) : null;", originalPropertyAttribute, propertyAttribute, field.Type.GetTypeName()));
|
||||||
|
_ = toJsonMapping.AppendLine(string.Format(" data['{0}'] = {1}!.toJson();", originalPropertyAttribute, propertyAttribute));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_ = fromJsonMapping.AppendLine(string.Format(" {1} = json['{0}'];", originalPropertyAttribute, propertyAttribute));
|
||||||
|
_ = toJsonMapping.AppendLine(string.Format(" data['{0}'] = {1};", originalPropertyAttribute, propertyAttribute));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.Fields.Any())
|
||||||
|
{
|
||||||
|
// Remove trailing comma
|
||||||
|
fields.Length--;
|
||||||
|
fields.Length--;
|
||||||
|
_ = fields.Append('}');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config.RemoveConstructors)
|
||||||
|
{
|
||||||
|
// Create Class Constructor
|
||||||
|
_ = sw.AppendLine();
|
||||||
|
_ = sw.AppendLine(string.Format(" {0}({1}); {2}", type.AssignedName, fields.ToString(), Environment.NewLine));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.Fields.Any())
|
||||||
|
{
|
||||||
|
if (!config.RemoveFromJson)
|
||||||
|
{
|
||||||
|
// Add From Json Function
|
||||||
|
_ = sw.AppendLine(string.Format(" {0}.fromJson(Map<String, dynamic> json) {{", type.AssignedName));
|
||||||
|
_ = sw.Append(fromJsonMapping.ToString());
|
||||||
|
_ = sw.AppendLine(" }");
|
||||||
|
|
||||||
|
_ = sw.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config.RemoveToJson)
|
||||||
|
{
|
||||||
|
// Add To Json Function
|
||||||
|
_ = sw.AppendLine(" Map<String, dynamic> toJson() {");
|
||||||
|
_ = sw.AppendLine(" final Map<String, dynamic> data = Map<String, dynamic>();");
|
||||||
|
_ = sw.Append(toJsonMapping.ToString());
|
||||||
|
_ = sw.AppendLine(" return data;");
|
||||||
|
_ = sw.AppendLine(" }");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteDeserializationComment(IJsonClassGeneratorConfig config, StringBuilder sw, bool rootIsArray = false)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteFileEnd(IJsonClassGeneratorConfig config, StringBuilder sw)
|
||||||
|
{
|
||||||
|
// sw.Insert(0, "import json" + Environment.NewLine);
|
||||||
|
// sw.Insert(0, "from dataclasses import dataclass" + Environment.NewLine);
|
||||||
|
// sw.Insert(0, "from typing import Any" + Environment.NewLine);
|
||||||
|
// if (sw.ToString().Contains("List"))
|
||||||
|
// sw.Insert(0, string.Format("from typing import List{0}", Environment.NewLine));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteFileStart(IJsonClassGeneratorConfig config, StringBuilder sw)
|
||||||
|
{
|
||||||
|
_ = sw.AppendLine("/* ");
|
||||||
|
_ = sw.AppendLine("// Example Usage");
|
||||||
|
_ = sw.AppendLine("Map<String, dynamic> map = jsonDecode(<myJSONString>);");
|
||||||
|
_ = sw.AppendLine("var myRootNode = Root.fromJson(map);");
|
||||||
|
_ = sw.AppendLine("*/ ");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteNamespaceEnd(IJsonClassGeneratorConfig config, StringBuilder sw, bool root)
|
||||||
|
{
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteNamespaceStart(IJsonClassGeneratorConfig config, StringBuilder sw, bool root)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetCollectionTypeName(string elementTypeName, OutputCollectionType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
//case OutputCollectionType.Array: return elementTypeName + "[]";
|
||||||
|
OutputCollectionType.Array => elementTypeName + "[]",
|
||||||
|
OutputCollectionType.MutableList => "List<" + elementTypeName + ">?",
|
||||||
|
// case OutputCollectionType.IReadOnlyList: return "IReadOnlyList<" + elementTypeName + ">";
|
||||||
|
// case OutputCollectionType.ImmutableArray: return "ImmutableArray<" + elementTypeName + ">";
|
||||||
|
_ => throw new ArgumentOutOfRangeException(paramName: nameof(type), actualValue: type, message: "Invalid " + nameof(OutputCollectionType) + " enum value."),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
176
Json2CSharpCodeGenerator.Lib/CodeWriters/JavaCodeWriter.cs
Normal file
176
Json2CSharpCodeGenerator.Lib/CodeWriters/JavaCodeWriter.cs
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Json2CSharpCodeGenerator.Lib.CodeWriters;
|
||||||
|
|
||||||
|
public class JavaCodeWriter : ICodeBuilder
|
||||||
|
{
|
||||||
|
public string FileExtension => ".java";
|
||||||
|
|
||||||
|
public string DisplayName => "Java";
|
||||||
|
|
||||||
|
private static readonly HashSet<string> _ReservedKeywords = new(comparer: StringComparer.Ordinal) {
|
||||||
|
// https://en.wikipedia.org/wiki/List_of_Java_keywords
|
||||||
|
// `Array.from( document.querySelectorAll('dl > dt > code') ).map( e => '"' + e.textContent + '"' ).sort().join( ", " )`
|
||||||
|
|
||||||
|
"abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "const", "continue",
|
||||||
|
"default", "do", "double", "else", "enum", "extends", "false", "final", "finally", "float", "for", "goto", "goto",
|
||||||
|
"if", "implements", "import", "instanceof", "int", "interface", "long", "native", "new", "non-sealed", "null",
|
||||||
|
"open", "opens", "package", "permits", "private", "protected", "provides", "public", "record", "return",
|
||||||
|
"sealed", "short", "static", "strictfp", "super", "switch", "synchronized",
|
||||||
|
"this", "throw", "throws", "to", "transient", "transitive", "true", "try",
|
||||||
|
"uses", "var", "void", "volatile", "while", "with", "yield"
|
||||||
|
};
|
||||||
|
|
||||||
|
IReadOnlyCollection<string> ICodeBuilder.ReservedKeywords => _ReservedKeywords;
|
||||||
|
public bool IsReservedKeyword(string word) => _ReservedKeywords.Contains(word ?? string.Empty);
|
||||||
|
|
||||||
|
public string GetTypeName(JsonType type, IJsonClassGeneratorConfig config)
|
||||||
|
{
|
||||||
|
return type.Type switch
|
||||||
|
{
|
||||||
|
JsonTypeEnum.Anything => "Object",
|
||||||
|
JsonTypeEnum.Array => GetCollectionTypeName(elementTypeName: GetTypeName(type.InternalType, config), config.CollectionType),
|
||||||
|
JsonTypeEnum.Dictionary => "HashMap<String, " + GetTypeName(type.InternalType, config) + ">",
|
||||||
|
JsonTypeEnum.Boolean => "boolean",
|
||||||
|
JsonTypeEnum.Float => "double",
|
||||||
|
JsonTypeEnum.Integer => "int",
|
||||||
|
JsonTypeEnum.NonConstrained => "Object",
|
||||||
|
JsonTypeEnum.NullableBoolean => "boolean",
|
||||||
|
JsonTypeEnum.NullableFloat => "double",
|
||||||
|
JsonTypeEnum.NullableInteger => "int",
|
||||||
|
JsonTypeEnum.NullableLong => "long",
|
||||||
|
JsonTypeEnum.NullableDate => "Date",
|
||||||
|
JsonTypeEnum.Long => "long",
|
||||||
|
JsonTypeEnum.Date => "Date",
|
||||||
|
JsonTypeEnum.NullableSomething => "Object",
|
||||||
|
JsonTypeEnum.Object => type.NewAssignedName,
|
||||||
|
JsonTypeEnum.String => "String",
|
||||||
|
_ => throw new NotSupportedException("Unsupported json type"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetCollectionTypeName(string elementTypeName, OutputCollectionType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
OutputCollectionType.Array => elementTypeName + "[]",
|
||||||
|
OutputCollectionType.MutableList => "ArrayList<" + elementTypeName + ">",
|
||||||
|
OutputCollectionType.IReadOnlyList or OutputCollectionType.ImmutableArray => throw new NotSupportedException("Java does not have in-box interfaces for read-only and immutable collections."),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(paramName: nameof(type), actualValue: type, message: "Invalid " + nameof(OutputCollectionType) + " enum value."),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteClass(IJsonClassGeneratorConfig config, StringBuilder sw, JsonType type)
|
||||||
|
{
|
||||||
|
string visibility = config.InternalVisibility ? "" : "public";
|
||||||
|
|
||||||
|
_ = sw.AppendFormat("{0} class {1}", visibility, type.AssignedName);
|
||||||
|
_ = sw.AppendLine("{");
|
||||||
|
|
||||||
|
string prefix = config.UseNestedClasses && !type.IsRoot ? "" : " ";
|
||||||
|
|
||||||
|
WriteClassMembers(config, sw, type, prefix);
|
||||||
|
|
||||||
|
if (config.UseNestedClasses && !type.IsRoot)
|
||||||
|
_ = sw.AppendLine(" }");
|
||||||
|
|
||||||
|
if (!config.UseNestedClasses)
|
||||||
|
_ = sw.AppendLine("}");
|
||||||
|
|
||||||
|
_ = sw.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteClassMembers(IJsonClassGeneratorConfig config, StringBuilder sw, JsonType type, string prefix)
|
||||||
|
{
|
||||||
|
foreach (FieldInfo field in type.Fields)
|
||||||
|
{
|
||||||
|
if (config.UsePascalCase || config.ExamplesInDocumentation)
|
||||||
|
_ = sw.AppendLine();
|
||||||
|
|
||||||
|
// Check if property name starts with number
|
||||||
|
string memberName = field.MemberName;
|
||||||
|
if (!string.IsNullOrEmpty(field.MemberName) && char.IsDigit(field.MemberName[0]))
|
||||||
|
memberName = "_" + memberName;
|
||||||
|
|
||||||
|
if (IsReservedKeyword(memberName))
|
||||||
|
memberName = "my" + memberName;
|
||||||
|
|
||||||
|
if (config.MutableClasses.Members == OutputMembers.AsProperties)
|
||||||
|
{
|
||||||
|
_ = sw.AppendFormat(prefix + "@JsonProperty" + "(\"{0}\") {1}", field.JsonMemberName, Environment.NewLine);
|
||||||
|
_ = sw.AppendFormat(prefix + "public {0} get{1}() {{ \r\t\t return this.{2}; }} {3}", field.Type.GetTypeName(), ChangeFirstChar(memberName), ChangeFirstChar(memberName, false), Environment.NewLine);
|
||||||
|
_ = sw.AppendFormat(prefix + "public void set{1}({0} {2}) {{ \r\t\t this.{2} = {2}; }} {3}", field.Type.GetTypeName(), ChangeFirstChar(memberName), ChangeFirstChar(memberName, false), Environment.NewLine);
|
||||||
|
_ = sw.AppendFormat(prefix + "{0} {1};", field.Type.GetTypeName(), ChangeFirstChar(memberName, false));
|
||||||
|
_ = sw.AppendLine();
|
||||||
|
}
|
||||||
|
else if (config.MutableClasses.Members == OutputMembers.AsPublicFields)
|
||||||
|
{
|
||||||
|
memberName = ChangeFirstChar(memberName, false);
|
||||||
|
if (field.JsonMemberName != memberName)
|
||||||
|
{
|
||||||
|
_ = sw.AppendFormat(prefix + "@JsonProperty" + "(\"{0}\") {1}", field.JsonMemberName, Environment.NewLine);
|
||||||
|
}
|
||||||
|
_ = sw.AppendFormat(prefix + "public {0} {1};{2}", field.Type.GetTypeName(), memberName, Environment.NewLine);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Unsupported " + nameof(OutputMembers) + " value: " + config.MutableClasses.Members);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ChangeFirstChar(string value, bool toCaptial = true)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
throw new ArgumentNullException("value");
|
||||||
|
if (value.Length == 0)
|
||||||
|
return value;
|
||||||
|
StringBuilder sb = new();
|
||||||
|
|
||||||
|
_ = sb.Append(toCaptial ? char.ToUpper(value[0]) : char.ToLower(value[0]));
|
||||||
|
_ = sb.Append(value.Substring(1));
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteFileStart(IJsonClassGeneratorConfig config, StringBuilder sw)
|
||||||
|
{
|
||||||
|
// foreach (var line in JsonClassGenerator.FileHeader)
|
||||||
|
// {
|
||||||
|
// sw.WriteLine("// " + line);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteFileEnd(IJsonClassGeneratorConfig config, StringBuilder sw)
|
||||||
|
{
|
||||||
|
if (config.UseNestedClasses)
|
||||||
|
{
|
||||||
|
_ = sw.AppendLine(" }");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteNamespaceStart(IJsonClassGeneratorConfig config, StringBuilder sw, bool root)
|
||||||
|
{
|
||||||
|
_ = sw.AppendLine();
|
||||||
|
_ = sw.AppendFormat("package {0};", root && !config.UseNestedClasses ? config.Namespace : (config.SecondaryNamespace ?? config.Namespace));
|
||||||
|
_ = sw.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteNamespaceEnd(IJsonClassGeneratorConfig config, StringBuilder sw, bool root) => sw.AppendLine("}");
|
||||||
|
|
||||||
|
public void WriteDeserializationComment(IJsonClassGeneratorConfig config, StringBuilder sw, bool rootIsArray = false)
|
||||||
|
{
|
||||||
|
_ = sw.AppendLine("// import com.fasterxml.jackson.databind.ObjectMapper; // version 2.11.1");
|
||||||
|
_ = sw.AppendLine("// import com.fasterxml.jackson.annotation.JsonProperty; // version 2.11.1");
|
||||||
|
_ = sw.AppendLine("/* ObjectMapper om = new ObjectMapper();");
|
||||||
|
|
||||||
|
if (rootIsArray)
|
||||||
|
{
|
||||||
|
_ = sw.AppendLine("Root[] root = om.readValue(myJsonString, Root[].class); */");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_ = sw.AppendLine("Root root = om.readValue(myJsonString, Root.class); */");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
155
Json2CSharpCodeGenerator.Lib/CodeWriters/PythonCodeWriter.cs
Normal file
155
Json2CSharpCodeGenerator.Lib/CodeWriters/PythonCodeWriter.cs
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Json2CSharpCodeGenerator.Lib.CodeWriters;
|
||||||
|
|
||||||
|
public class PythonCodeWriter : ICodeBuilder
|
||||||
|
{
|
||||||
|
public string FileExtension => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public string DisplayName => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public IReadOnlyCollection<string> ReservedKeywords => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public string GetTypeName(JsonType type, IJsonClassGeneratorConfig config)
|
||||||
|
{
|
||||||
|
return type.Type switch
|
||||||
|
{
|
||||||
|
JsonTypeEnum.Anything => "object",
|
||||||
|
JsonTypeEnum.Array => GetCollectionTypeName(elementTypeName: GetTypeName(type.InternalType, config), config.CollectionType),
|
||||||
|
// case JsonTypeEnum.Dictionary: return "Dictionary<string, " + this.GetTypeName(type.InternalType, config) + ">";
|
||||||
|
JsonTypeEnum.Boolean => "bool",
|
||||||
|
JsonTypeEnum.Float => "float",
|
||||||
|
JsonTypeEnum.Integer => "int",
|
||||||
|
JsonTypeEnum.Long => "float",
|
||||||
|
//case JsonTypeEnum.Date: return "DateTime"; // Do Later
|
||||||
|
JsonTypeEnum.Date => "str",
|
||||||
|
JsonTypeEnum.NonConstrained => "object",
|
||||||
|
// case JsonTypeEnum.NullableBoolean: return "bool?";
|
||||||
|
//case JsonTypeEnum.NullableFloat: return "double?";
|
||||||
|
//case JsonTypeEnum.NullableInteger: return "int?";
|
||||||
|
//case JsonTypeEnum.NullableLong: return "long?";
|
||||||
|
//case JsonTypeEnum.NullableDate: return "DateTime?";
|
||||||
|
//case JsonTypeEnum.NullableSomething: return "object";
|
||||||
|
JsonTypeEnum.Object => type.NewAssignedName,
|
||||||
|
JsonTypeEnum.String => "str",
|
||||||
|
_ => throw new NotSupportedException("Unsupported json type: " + type.Type),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetTypeFunctionName(JsonType type, IJsonClassGeneratorConfig config)
|
||||||
|
{
|
||||||
|
return type.Type switch
|
||||||
|
{
|
||||||
|
JsonTypeEnum.Anything => "",
|
||||||
|
JsonTypeEnum.Array => "[" + type.InternalType.NewAssignedName + ".from_dict(y) for y in {0}]",
|
||||||
|
// case JsonTypeEnum.Dictionary: return "Dictionary<string, " + this.GetTypeName(type.InternalType, config) + ">";
|
||||||
|
JsonTypeEnum.Boolean => "",
|
||||||
|
JsonTypeEnum.Float => "float({0})",
|
||||||
|
JsonTypeEnum.Integer => "int({0})",
|
||||||
|
JsonTypeEnum.Long => "float({0})",
|
||||||
|
//case JsonTypeEnum.Date: return "DateTime"; // Do Later
|
||||||
|
JsonTypeEnum.Date => "str({0})",
|
||||||
|
JsonTypeEnum.NonConstrained => "",
|
||||||
|
// case JsonTypeEnum.NullableBoolean: return "bool?";
|
||||||
|
//case JsonTypeEnum.NullableFloat: return "double?";
|
||||||
|
//case JsonTypeEnum.NullableInteger: return "int?";
|
||||||
|
//case JsonTypeEnum.NullableLong: return "long?";
|
||||||
|
//case JsonTypeEnum.NullableDate: return "DateTime?";
|
||||||
|
//case JsonTypeEnum.NullableSomething: return "object";
|
||||||
|
JsonTypeEnum.Object => type.NewAssignedName + ".from_dict({0})",
|
||||||
|
JsonTypeEnum.String => "str({0})",
|
||||||
|
_ => throw new NotSupportedException("Unsupported json type: " + type.Type),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsReservedKeyword(string word) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public void WriteClass(IJsonClassGeneratorConfig config, StringBuilder sw, JsonType type)
|
||||||
|
{
|
||||||
|
string className = type.AssignedName;
|
||||||
|
|
||||||
|
_ = sw.AppendLine("@dataclass");
|
||||||
|
_ = sw.AppendFormat("class {0}:{1}", className, Environment.NewLine);
|
||||||
|
WriteClassMembers(config, sw, type, "");
|
||||||
|
_ = sw.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteClassMembers(IJsonClassGeneratorConfig config, StringBuilder sw, JsonType type, string prefix)
|
||||||
|
{
|
||||||
|
StringBuilder fields = new();
|
||||||
|
StringBuilder mappingFunction = new();
|
||||||
|
foreach (FieldInfo field in type.Fields)
|
||||||
|
{
|
||||||
|
_ = field.MemberName;
|
||||||
|
string propertyAttribute = field.JsonMemberName;
|
||||||
|
string internalPropertyAttribute = "_" + propertyAttribute;
|
||||||
|
_ = sw.AppendFormat(" {0}: {1}{2}", propertyAttribute, field.Type.GetTypeName(), Environment.NewLine);
|
||||||
|
|
||||||
|
string mappingFragment = string.Format("obj.get(\"{0}\")", propertyAttribute);
|
||||||
|
string mappingFragment2 = string.Format(GetTypeFunctionName(field.Type, config), mappingFragment);
|
||||||
|
string mappingString = string.Format(" {0} = {1}", internalPropertyAttribute, mappingFragment2);
|
||||||
|
_ = mappingFunction.AppendLine(mappingString);
|
||||||
|
_ = fields.Append(internalPropertyAttribute + ", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove trailing comma
|
||||||
|
fields.Length--;
|
||||||
|
fields.Length--;
|
||||||
|
|
||||||
|
// Write Dictionnary Mapping Functions
|
||||||
|
_ = sw.AppendLine();
|
||||||
|
_ = sw.AppendLine(" @staticmethod");
|
||||||
|
_ = sw.AppendLine(string.Format(" def from_dict(obj: Any) -> '{0}':", type.AssignedName));
|
||||||
|
_ = sw.Append(mappingFunction.ToString());
|
||||||
|
_ = sw.AppendLine(string.Format(" return {0}({1})", type.AssignedName, fields.ToString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteDeserializationComment(IJsonClassGeneratorConfig config, StringBuilder sw, bool rootIsArray = false)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteFileEnd(IJsonClassGeneratorConfig config, StringBuilder sw)
|
||||||
|
{
|
||||||
|
_ = sw.Insert(0, "9 json" + Environment.NewLine);
|
||||||
|
_ = sw.Insert(0, "from dataclasses import dataclass" + Environment.NewLine);
|
||||||
|
_ = sw.Insert(0, "from typing import Any" + Environment.NewLine);
|
||||||
|
if (sw.ToString().Contains("List"))
|
||||||
|
_ = sw.Insert(0, string.Format("from typing import List{0}", Environment.NewLine));
|
||||||
|
|
||||||
|
_ = sw.AppendLine("# Example Usage");
|
||||||
|
_ = sw.AppendLine("# jsonstring = json.loads(myjsonstring)");
|
||||||
|
_ = sw.AppendLine("# root = Root.from_dict(jsonstring)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteFileStart(IJsonClassGeneratorConfig config, StringBuilder sw)
|
||||||
|
{
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteNamespaceEnd(IJsonClassGeneratorConfig config, StringBuilder sw, bool root)
|
||||||
|
{
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteNamespaceStart(IJsonClassGeneratorConfig config, StringBuilder sw, bool root)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetCollectionTypeName(string elementTypeName, OutputCollectionType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
//case OutputCollectionType.Array: return elementTypeName + "[]";
|
||||||
|
OutputCollectionType.Array => "List[" + elementTypeName + "]",
|
||||||
|
OutputCollectionType.MutableList => "List[" + elementTypeName + "]",
|
||||||
|
// case OutputCollectionType.IReadOnlyList: return "IReadOnlyList<" + elementTypeName + ">";
|
||||||
|
// case OutputCollectionType.ImmutableArray: return "ImmutableArray<" + elementTypeName + ">";
|
||||||
|
_ => throw new ArgumentOutOfRangeException(paramName: nameof(type), actualValue: type, message: "Invalid " + nameof(OutputCollectionType) + " enum value."),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,97 @@
|
|||||||
|
namespace Json2CSharpCodeGenerator.Lib.CodeWriters;
|
||||||
|
|
||||||
|
public class TypeScriptCodeWriter : ICodeWriter
|
||||||
|
{
|
||||||
|
public string FileExtension => ".ts";
|
||||||
|
|
||||||
|
public string DisplayName => "TypeScript";
|
||||||
|
|
||||||
|
public string GetTypeName(JsonType type, IJsonClassGeneratorConfig config)
|
||||||
|
{
|
||||||
|
return type.Type switch
|
||||||
|
{
|
||||||
|
JsonTypeEnum.Anything => "any",
|
||||||
|
JsonTypeEnum.String => "string",
|
||||||
|
JsonTypeEnum.Boolean => "bool",
|
||||||
|
JsonTypeEnum.Integer or JsonTypeEnum.Long or JsonTypeEnum.Float => "number",
|
||||||
|
JsonTypeEnum.Date => "Date",
|
||||||
|
JsonTypeEnum.NullableInteger or JsonTypeEnum.NullableLong or JsonTypeEnum.NullableFloat => "number",
|
||||||
|
JsonTypeEnum.NullableBoolean => "bool",
|
||||||
|
JsonTypeEnum.NullableDate => "Date",
|
||||||
|
JsonTypeEnum.Object => type.AssignedName,
|
||||||
|
JsonTypeEnum.Array => GetTypeName(type.InternalType, config) + "[]",
|
||||||
|
JsonTypeEnum.Dictionary => "{ [key: string]: " + GetTypeName(type.InternalType, config) + "; }",
|
||||||
|
JsonTypeEnum.NullableSomething => "any",
|
||||||
|
JsonTypeEnum.NonConstrained => "any",
|
||||||
|
_ => throw new NotSupportedException("Unsupported type"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteClass(IJsonClassGeneratorConfig config, TextWriter sw, JsonType type)
|
||||||
|
{
|
||||||
|
string prefix = GetNamespace(config, type.IsRoot) != null ? " " : "";
|
||||||
|
bool exported = !config.InternalVisibility || config.SecondaryNamespace != null;
|
||||||
|
sw.WriteLine(prefix + (exported ? "export " : string.Empty) + "interface " + type.AssignedName + " {");
|
||||||
|
foreach (FieldInfo field in type.Fields)
|
||||||
|
{
|
||||||
|
bool shouldDefineNamespace = type.IsRoot && config.SecondaryNamespace != null && config.Namespace != null && (field.Type.Type == JsonTypeEnum.Object || (field.Type.InternalType != null && field.Type.InternalType.Type == JsonTypeEnum.Object));
|
||||||
|
if (config.ExamplesInDocumentation)
|
||||||
|
{
|
||||||
|
sw.WriteLine();
|
||||||
|
sw.WriteLine(prefix + " /**");
|
||||||
|
sw.WriteLine(prefix + " * Examples: " + field.GetExamplesText());
|
||||||
|
sw.WriteLine(prefix + " */");
|
||||||
|
}
|
||||||
|
|
||||||
|
sw.WriteLine(prefix + " " + field.JsonMemberName + (IsNullable(field.Type.Type) ? "?" : "") + ": " + (shouldDefineNamespace ? config.SecondaryNamespace + "." : string.Empty) + GetTypeName(field.Type, config) + ";");
|
||||||
|
}
|
||||||
|
sw.WriteLine(prefix + "}");
|
||||||
|
sw.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsNullable(JsonTypeEnum type)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
type is JsonTypeEnum.NullableBoolean or
|
||||||
|
JsonTypeEnum.NullableDate or
|
||||||
|
JsonTypeEnum.NullableFloat or
|
||||||
|
JsonTypeEnum.NullableInteger or
|
||||||
|
JsonTypeEnum.NullableLong or
|
||||||
|
JsonTypeEnum.NullableSomething;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteFileStart(IJsonClassGeneratorConfig config, TextWriter sw)
|
||||||
|
{
|
||||||
|
// foreach (var line in JsonClassGenerator.FileHeader)
|
||||||
|
// {
|
||||||
|
// sw.WriteLine("// " + line);
|
||||||
|
// }
|
||||||
|
// sw.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteFileEnd(IJsonClassGeneratorConfig config, TextWriter sw)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetNamespace(IJsonClassGeneratorConfig config, bool root) => root ? config.Namespace : (config.SecondaryNamespace ?? config.Namespace);
|
||||||
|
|
||||||
|
public void WriteNamespaceStart(IJsonClassGeneratorConfig config, TextWriter sw, bool root)
|
||||||
|
{
|
||||||
|
if (GetNamespace(config, root) != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
sw.WriteLine("module " + GetNamespace(config, root) + " {");
|
||||||
|
sw.WriteLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteNamespaceEnd(IJsonClassGeneratorConfig config, TextWriter sw, bool root)
|
||||||
|
{
|
||||||
|
if (GetNamespace(config, root) != null)
|
||||||
|
{
|
||||||
|
sw.WriteLine("}");
|
||||||
|
sw.WriteLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,161 @@
|
|||||||
|
namespace Json2CSharpCodeGenerator.Lib.CodeWriters;
|
||||||
|
|
||||||
|
public class VisualBasicCodeWriter : ICodeWriter
|
||||||
|
{
|
||||||
|
public string FileExtension => ".vb";
|
||||||
|
|
||||||
|
public string DisplayName => "Visual Basic .NET";
|
||||||
|
|
||||||
|
private const string _NoRenameAttribute = "<Obfuscation(Feature:=\"renaming\", Exclude:=true)>";
|
||||||
|
private const string _NoPruneAttribute = "<Obfuscation(Feature:=\"trigger\", Exclude:=false)>";
|
||||||
|
|
||||||
|
public string GetTypeName(JsonType type, IJsonClassGeneratorConfig config)
|
||||||
|
{
|
||||||
|
return type.Type switch
|
||||||
|
{
|
||||||
|
JsonTypeEnum.Anything => "Object",
|
||||||
|
JsonTypeEnum.Array => GetCollectionTypeName(GetTypeName(type.InternalType, config), config.CollectionType),
|
||||||
|
JsonTypeEnum.Dictionary => "Dictionary(Of String, " + GetTypeName(type.InternalType, config) + ")",
|
||||||
|
JsonTypeEnum.Boolean => "Boolean",
|
||||||
|
JsonTypeEnum.Float => "Double",
|
||||||
|
JsonTypeEnum.Integer => "Integer",
|
||||||
|
JsonTypeEnum.Long => "Long",
|
||||||
|
JsonTypeEnum.Date => "DateTime",
|
||||||
|
JsonTypeEnum.NonConstrained => "Object",
|
||||||
|
JsonTypeEnum.NullableBoolean => "Boolean?",
|
||||||
|
JsonTypeEnum.NullableFloat => "Double?",
|
||||||
|
JsonTypeEnum.NullableInteger => "Integer?",
|
||||||
|
JsonTypeEnum.NullableLong => "Long?",
|
||||||
|
JsonTypeEnum.NullableDate => "DateTime?",
|
||||||
|
JsonTypeEnum.NullableSomething => "Object",
|
||||||
|
JsonTypeEnum.Object => type.AssignedName,
|
||||||
|
JsonTypeEnum.String => "String",
|
||||||
|
_ => throw new NotSupportedException("Unsupported json type"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetCollectionTypeName(string elementTypeName, OutputCollectionType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
OutputCollectionType.Array => elementTypeName + "()",
|
||||||
|
OutputCollectionType.MutableList => "List(Of" + elementTypeName + ")",
|
||||||
|
OutputCollectionType.IReadOnlyList => "IReadOnlyList(Of" + elementTypeName + ")",
|
||||||
|
OutputCollectionType.ImmutableArray => "ImmutableArray(Of" + elementTypeName + ")",
|
||||||
|
_ => throw new ArgumentOutOfRangeException(paramName: nameof(type), actualValue: type, message: "Invalid " + nameof(OutputCollectionType) + " enum value."),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ShouldApplyNoRenamingAttribute(IJsonClassGeneratorConfig config) => config.ApplyObfuscationAttributes && !config.UsePascalCase;
|
||||||
|
|
||||||
|
private bool ShouldApplyNoPruneAttribute(IJsonClassGeneratorConfig config) => config.ApplyObfuscationAttributes && config.OutputType == OutputTypes.MutableClass && config.MutableClasses.Members == OutputMembers.AsPublicFields;
|
||||||
|
|
||||||
|
public void WriteClass(IJsonClassGeneratorConfig config, TextWriter sw, JsonType type)
|
||||||
|
{
|
||||||
|
string visibility = config.InternalVisibility ? "Friend" : "Public";
|
||||||
|
|
||||||
|
if (config.UseNestedClasses)
|
||||||
|
{
|
||||||
|
sw.WriteLine(" {0} Partial Class {1}", visibility, config.MainClass);
|
||||||
|
if (!type.IsRoot)
|
||||||
|
{
|
||||||
|
if (ShouldApplyNoRenamingAttribute(config))
|
||||||
|
sw.WriteLine(" " + _NoRenameAttribute);
|
||||||
|
if (ShouldApplyNoPruneAttribute(config))
|
||||||
|
sw.WriteLine(" " + _NoPruneAttribute);
|
||||||
|
sw.WriteLine(" {0} Class {1}", visibility, type.AssignedName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ShouldApplyNoRenamingAttribute(config))
|
||||||
|
sw.WriteLine(" " + _NoRenameAttribute);
|
||||||
|
if (ShouldApplyNoPruneAttribute(config))
|
||||||
|
sw.WriteLine(" " + _NoPruneAttribute);
|
||||||
|
sw.WriteLine(" {0} Class {1}", visibility, type.AssignedName);
|
||||||
|
}
|
||||||
|
|
||||||
|
string prefix = config.UseNestedClasses && !type.IsRoot ? " " : " ";
|
||||||
|
|
||||||
|
WriteClassMembers(config, sw, type, prefix);
|
||||||
|
|
||||||
|
if (config.UseNestedClasses && !type.IsRoot)
|
||||||
|
sw.WriteLine(" End Class");
|
||||||
|
|
||||||
|
sw.WriteLine(" End Class");
|
||||||
|
sw.WriteLine();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteClassMembers(IJsonClassGeneratorConfig config, TextWriter sw, JsonType type, string prefix)
|
||||||
|
{
|
||||||
|
|
||||||
|
foreach (FieldInfo field in type.Fields)
|
||||||
|
{
|
||||||
|
if (config.UsePascalCase || config.ExamplesInDocumentation)
|
||||||
|
sw.WriteLine();
|
||||||
|
|
||||||
|
if (config.ExamplesInDocumentation)
|
||||||
|
{
|
||||||
|
sw.WriteLine(prefix + "''' <summary>");
|
||||||
|
sw.WriteLine(prefix + "''' Examples: " + field.GetExamplesText());
|
||||||
|
sw.WriteLine(prefix + "''' </summary>");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.UsePascalCase)
|
||||||
|
{
|
||||||
|
sw.WriteLine(prefix + "<JsonProperty(\"{0}\")>", field.JsonMemberName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.MutableClasses.Members == OutputMembers.AsProperties)
|
||||||
|
{
|
||||||
|
sw.WriteLine(prefix + "Public Property {1} As {0}", field.Type.GetTypeName(), field.MemberName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sw.WriteLine(prefix + "Public {1} As {0}", field.Type.GetTypeName(), field.MemberName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteFileStart(IJsonClassGeneratorConfig config, TextWriter sw)
|
||||||
|
{
|
||||||
|
sw.WriteLine();
|
||||||
|
sw.WriteLine("Imports System");
|
||||||
|
sw.WriteLine("Imports System.Collections.Generic");
|
||||||
|
|
||||||
|
if (ShouldApplyNoRenamingAttribute(config) || ShouldApplyNoPruneAttribute(config))
|
||||||
|
{
|
||||||
|
sw.WriteLine("Imports System.Reflection");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.AttributeLibrary == JsonLibrary.NewtonsoftJson)
|
||||||
|
{
|
||||||
|
sw.WriteLine("Imports Newtonsoft.Json");
|
||||||
|
sw.WriteLine("Imports Newtonsoft.Json.Linq");
|
||||||
|
}
|
||||||
|
else if (config.AttributeLibrary == JsonLibrary.SystemTextJson)
|
||||||
|
{
|
||||||
|
sw.WriteLine("Imports System.Text.Json");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(config.SecondaryNamespace) && !config.UseNestedClasses)
|
||||||
|
{
|
||||||
|
sw.WriteLine("Imports {0}", config.SecondaryNamespace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteFileEnd(IJsonClassGeneratorConfig config, TextWriter sw)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteNamespaceStart(IJsonClassGeneratorConfig config, TextWriter sw, bool root)
|
||||||
|
{
|
||||||
|
sw.WriteLine();
|
||||||
|
sw.WriteLine("Namespace Global.{0}", root && !config.UseNestedClasses ? config.Namespace : (config.SecondaryNamespace ?? config.Namespace));
|
||||||
|
sw.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteNamespaceEnd(IJsonClassGeneratorConfig config, TextWriter sw, bool root) => sw.WriteLine("End Namespace");
|
||||||
|
}
|
196
Json2CSharpCodeGenerator.Lib/CodeWriters/php.txt
Normal file
196
Json2CSharpCodeGenerator.Lib/CodeWriters/php.txt
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Xamasoft.JsonClassGenerator.CodeWriters
|
||||||
|
{
|
||||||
|
public class PhpCodeWriter : ICodeWriter
|
||||||
|
{
|
||||||
|
public string FileExtension
|
||||||
|
{
|
||||||
|
get { return ".php"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string DisplayName
|
||||||
|
{
|
||||||
|
get { return "PHP"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetTypeName(JsonType type, IJsonClassGeneratorConfig config)
|
||||||
|
{
|
||||||
|
var arraysAsLists = !config.ExplicitDeserialization;
|
||||||
|
|
||||||
|
switch (type.Type)
|
||||||
|
{
|
||||||
|
case JsonTypeEnum.Anything: return "Object";
|
||||||
|
case JsonTypeEnum.Array: return "array(" + GetTypeName(type.InternalType, config) + ")";
|
||||||
|
case JsonTypeEnum.Boolean: return "boolean";
|
||||||
|
case JsonTypeEnum.Float: return "double";
|
||||||
|
case JsonTypeEnum.Integer: return "int";
|
||||||
|
case JsonTypeEnum.Long: return "long";
|
||||||
|
case JsonTypeEnum.Date: return "Date";
|
||||||
|
case JsonTypeEnum.NonConstrained: return "Object";
|
||||||
|
case JsonTypeEnum.NullableBoolean: return "bool?";
|
||||||
|
case JsonTypeEnum.NullableFloat: return "double?";
|
||||||
|
case JsonTypeEnum.NullableInteger: return "int?";
|
||||||
|
case JsonTypeEnum.NullableLong: return "long?";
|
||||||
|
case JsonTypeEnum.NullableDate: return "DateTime?";
|
||||||
|
case JsonTypeEnum.NullableSomething: return "object";
|
||||||
|
case JsonTypeEnum.Object: return type.AssignedName;
|
||||||
|
case JsonTypeEnum.String: return "String";
|
||||||
|
default: throw new System.NotSupportedException("Unsupported json type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteClass(IJsonClassGeneratorConfig config, TextWriter sw, JsonType type)
|
||||||
|
{
|
||||||
|
//var visibility = config.InternalVisibility ? "" : "public";
|
||||||
|
|
||||||
|
//if (config.UseNestedClasses)
|
||||||
|
//{
|
||||||
|
// if (!type.IsRoot)
|
||||||
|
// {
|
||||||
|
// if (config.PropertyAttribute == "DataMember")
|
||||||
|
// {
|
||||||
|
// sw.WriteLine(" [DataContract]");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (ShouldApplyNoRenamingAttribute(config)) sw.WriteLine(" " + NoRenameAttribute);
|
||||||
|
// if (ShouldApplyNoPruneAttribute(config)) sw.WriteLine(" " + NoPruneAttribute);
|
||||||
|
// sw.WriteLine(" {0} class {1}", visibility, type.AssignedName);
|
||||||
|
// sw.WriteLine(" {");
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// if (config.PropertyAttribute == "DataMember")
|
||||||
|
// {
|
||||||
|
// sw.WriteLine(" [DataContract]");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (ShouldApplyNoRenamingAttribute(config)) sw.WriteLine(" " + NoRenameAttribute);
|
||||||
|
// if (ShouldApplyNoPruneAttribute(config)) sw.WriteLine(" " + NoPruneAttribute);
|
||||||
|
sw.WriteLine("class {0}", type.AssignedName);
|
||||||
|
sw.WriteLine("{");
|
||||||
|
//}
|
||||||
|
|
||||||
|
var prefix = config.UseNestedClasses && !type.IsRoot ? "" : " ";
|
||||||
|
|
||||||
|
|
||||||
|
var shouldSuppressWarning = config.InternalVisibility && !config.UseProperties && !config.ExplicitDeserialization;
|
||||||
|
if (shouldSuppressWarning)
|
||||||
|
{
|
||||||
|
sw.WriteLine("#pragma warning disable 0649");
|
||||||
|
if (!config.UsePascalCase) sw.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (type.IsRoot && config.ExplicitDeserialization) WriteStringConstructorExplicitDeserialization(config, sw, type, prefix);
|
||||||
|
|
||||||
|
//if (config.ExplicitDeserialization)
|
||||||
|
//{
|
||||||
|
// if (config.UseProperties) WriteClassWithPropertiesExplicitDeserialization(sw, type, prefix);
|
||||||
|
// else WriteClassWithFieldsExplicitDeserialization(sw, type, prefix);
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
WriteClassMembers(config, sw, type, prefix);
|
||||||
|
//}
|
||||||
|
|
||||||
|
if (shouldSuppressWarning)
|
||||||
|
{
|
||||||
|
sw.WriteLine();
|
||||||
|
sw.WriteLine("#pragma warning restore 0649");
|
||||||
|
sw.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (config.UseNestedClasses && !type.IsRoot)
|
||||||
|
sw.WriteLine(" }");
|
||||||
|
|
||||||
|
if (!config.UseNestedClasses)
|
||||||
|
sw.WriteLine("}");
|
||||||
|
|
||||||
|
sw.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteFileStart(IJsonClassGeneratorConfig config, TextWriter sw)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteFileEnd(IJsonClassGeneratorConfig config, TextWriter sw)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteNamespaceStart(IJsonClassGeneratorConfig config, TextWriter sw, bool root)
|
||||||
|
{
|
||||||
|
sw.WriteLine("<?php");
|
||||||
|
sw.Write("\r");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteNamespaceEnd(IJsonClassGeneratorConfig config, TextWriter sw, bool root)
|
||||||
|
{
|
||||||
|
sw.Write("\r");
|
||||||
|
sw.WriteLine("?>");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteClassMembers(IJsonClassGeneratorConfig config, TextWriter sw, JsonType type, string prefix)
|
||||||
|
{
|
||||||
|
foreach (var field in type.Fields)
|
||||||
|
{
|
||||||
|
//if (config.UsePascalCase || config.ExamplesInDocumentation) sw.WriteLine();
|
||||||
|
|
||||||
|
//if (config.ExamplesInDocumentation)
|
||||||
|
//{
|
||||||
|
// sw.WriteLine(prefix + "/// <summary>");
|
||||||
|
// sw.WriteLine(prefix + "/// Examples: " + field.GetExamplesText());
|
||||||
|
// sw.WriteLine(prefix + "/// </summary>");
|
||||||
|
//}
|
||||||
|
|
||||||
|
//if (config.UsePascalCase || config.PropertyAttribute != "None")
|
||||||
|
//{
|
||||||
|
// if (config.UsePascalCase && config.PropertyAttribute == "None")
|
||||||
|
// sw.WriteLine(prefix + "@JsonProperty(\"{0}\")", field.JsonMemberName);
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// //if (config.PropertyAttribute == "DataMember")
|
||||||
|
// // sw.WriteLine(prefix + "[" + config.PropertyAttribute + "(Name=\"{0}\")]", field.JsonMemberName);
|
||||||
|
// if (config.PropertyAttribute == "JsonProperty")
|
||||||
|
// sw.WriteLine(prefix + "@" + config.PropertyAttribute + "(\"{0}\")", field.JsonMemberName);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
if (config.UseProperties)
|
||||||
|
{
|
||||||
|
//sw.WriteLine(prefix + "@JsonProperty" + "(\"{0}\")", field.JsonMemberName);
|
||||||
|
sw.WriteLine(prefix + "public function get{0}() {{ \r\t\t return $this->{1} \r\t}}", ChangeFirstChar(field.MemberName), field.MemberName);
|
||||||
|
sw.WriteLine(prefix + "public function set{0}(${1}) {{ \r\t\t $this->{1} = ${1} \r\t}}", ChangeFirstChar(field.MemberName), field.MemberName);
|
||||||
|
sw.WriteLine(prefix + "public ${1}; //{0}", field.Type.GetTypeName(), field.MemberName);
|
||||||
|
sw.WriteLine();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sw.WriteLine(prefix + "public ${1}; //{0}", field.Type.GetTypeName(), field.MemberName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ChangeFirstChar(string value, bool toCaptial = true)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
throw new ArgumentNullException("value");
|
||||||
|
if (value.Length == 0)
|
||||||
|
return value;
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb.Append(toCaptial ? char.ToUpper(value[0]) : char.ToLower(value[0]));
|
||||||
|
sb.Append(value.Substring(1));
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
98
Json2CSharpCodeGenerator.Lib/CodeWriters/sql.txt
Normal file
98
Json2CSharpCodeGenerator.Lib/CodeWriters/sql.txt
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Xamasoft.JsonClassGenerator.CodeWriters
|
||||||
|
{
|
||||||
|
public class SqlCodeWriter : ICodeWriter
|
||||||
|
{
|
||||||
|
public string FileExtension
|
||||||
|
{
|
||||||
|
get { return ".cs"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string DisplayName
|
||||||
|
{
|
||||||
|
get { return "SQL"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetTypeName(JsonType type, IJsonClassGeneratorConfig config)
|
||||||
|
{
|
||||||
|
var arraysAsLists = !config.ExplicitDeserialization;
|
||||||
|
|
||||||
|
switch (type.Type)
|
||||||
|
{
|
||||||
|
case JsonTypeEnum.Anything: return "object";
|
||||||
|
case JsonTypeEnum.Array: return arraysAsLists ? "IList<" + GetTypeName(type.InternalType, config) + ">" : GetTypeName(type.InternalType, config) + "[]";
|
||||||
|
case JsonTypeEnum.Dictionary: return "Dictionary<string, " + GetTypeName(type.InternalType, config) + ">";
|
||||||
|
case JsonTypeEnum.Boolean: return "bit NOT NULL";
|
||||||
|
case JsonTypeEnum.Float: return "[decimal](9,2) NOT NULL";
|
||||||
|
case JsonTypeEnum.Integer: return "[int] NOT NULL";
|
||||||
|
case JsonTypeEnum.Long: return "[bigint] NOT NULL";
|
||||||
|
case JsonTypeEnum.Date: return "[datetime]";
|
||||||
|
case JsonTypeEnum.NonConstrained: return "object";
|
||||||
|
case JsonTypeEnum.NullableBoolean: return "bit NULL";
|
||||||
|
case JsonTypeEnum.NullableFloat: return "[decimal](9,2) NULL";
|
||||||
|
case JsonTypeEnum.NullableInteger: return "[int] NULL";
|
||||||
|
case JsonTypeEnum.NullableLong: return "[bigint] NULL";
|
||||||
|
case JsonTypeEnum.NullableDate: return "[datetime] NULL";
|
||||||
|
case JsonTypeEnum.NullableSomething: return "object NULL";
|
||||||
|
case JsonTypeEnum.Object: return type.AssignedName;
|
||||||
|
case JsonTypeEnum.String: return "[varchar](50) NULL";
|
||||||
|
default: throw new System.NotSupportedException("Unsupported json type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteFileStart(IJsonClassGeneratorConfig config, TextWriter sw)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteFileEnd(IJsonClassGeneratorConfig config, TextWriter sw)
|
||||||
|
{
|
||||||
|
if (config.UseNestedClasses)
|
||||||
|
{
|
||||||
|
sw.WriteLine(" }");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteNamespaceStart(IJsonClassGeneratorConfig config, TextWriter sw, bool root)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteNamespaceEnd(IJsonClassGeneratorConfig config, TextWriter sw, bool root)
|
||||||
|
{
|
||||||
|
//sw.WriteLine("}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteClass(IJsonClassGeneratorConfig config, TextWriter sw, JsonType type)
|
||||||
|
{
|
||||||
|
sw.WriteLine("create table " + type.AssignedName + " (");
|
||||||
|
sw.WriteLine(" [Id] [int] IDENTITY(1,1) NOT NULL,");
|
||||||
|
|
||||||
|
WriteClassMembers(config, sw, type);
|
||||||
|
|
||||||
|
sw.WriteLine("CONSTRAINT [PK_" + type.AssignedName + "] PRIMARY KEY CLUSTERED");
|
||||||
|
sw.WriteLine(" (");
|
||||||
|
sw.WriteLine(" [Id] asc");
|
||||||
|
sw.WriteLine(" )");
|
||||||
|
sw.WriteLine(")");
|
||||||
|
|
||||||
|
sw.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteClassMembers(IJsonClassGeneratorConfig config, TextWriter sw, JsonType type)
|
||||||
|
{
|
||||||
|
foreach (var field in type.Fields)
|
||||||
|
{
|
||||||
|
if (config.UseProperties)
|
||||||
|
{
|
||||||
|
string typeName = field.Type.InternalType == null
|
||||||
|
? field.Type.GetTypeName()
|
||||||
|
: field.Type.InternalType.GetTypeName();
|
||||||
|
|
||||||
|
sw.WriteLine(" [{0}] {1},", field.MemberName, typeName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
118
Json2CSharpCodeGenerator.Lib/CodeWriters/typescript.txt
Normal file
118
Json2CSharpCodeGenerator.Lib/CodeWriters/typescript.txt
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Xamasoft.JsonClassGenerator.CodeWriters
|
||||||
|
{
|
||||||
|
public class TypeScriptCodeWriter : ICodeWriter
|
||||||
|
{
|
||||||
|
public string FileExtension
|
||||||
|
{
|
||||||
|
get { return ".ts"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string DisplayName
|
||||||
|
{
|
||||||
|
get { return "TypeScript"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetTypeName(JsonType type, IJsonClassGeneratorConfig config)
|
||||||
|
{
|
||||||
|
switch (type.Type)
|
||||||
|
{
|
||||||
|
case JsonTypeEnum.Anything: return "any";
|
||||||
|
case JsonTypeEnum.String: return "string";
|
||||||
|
case JsonTypeEnum.Boolean: return "bool";
|
||||||
|
case JsonTypeEnum.Integer:
|
||||||
|
case JsonTypeEnum.Long:
|
||||||
|
case JsonTypeEnum.Float: return "number";
|
||||||
|
case JsonTypeEnum.Date: return "Date";
|
||||||
|
case JsonTypeEnum.NullableInteger:
|
||||||
|
case JsonTypeEnum.NullableLong:
|
||||||
|
case JsonTypeEnum.NullableFloat: return "number";
|
||||||
|
case JsonTypeEnum.NullableBoolean: return "bool";
|
||||||
|
case JsonTypeEnum.NullableDate: return "Date";
|
||||||
|
case JsonTypeEnum.Object: return type.AssignedName;
|
||||||
|
case JsonTypeEnum.Array: return GetTypeName(type.InternalType, config) + "[]";
|
||||||
|
case JsonTypeEnum.Dictionary: return "{ [key: string]: " + GetTypeName(type.InternalType, config) + "; }";
|
||||||
|
case JsonTypeEnum.NullableSomething: return "any";
|
||||||
|
case JsonTypeEnum.NonConstrained: return "any";
|
||||||
|
default: throw new NotSupportedException("Unsupported type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteClass(IJsonClassGeneratorConfig config, TextWriter sw, JsonType type)
|
||||||
|
{
|
||||||
|
var prefix = GetNamespace(config, type.IsRoot) != null ? " " : "";
|
||||||
|
var exported = !config.InternalVisibility || config.SecondaryNamespace != null;
|
||||||
|
sw.WriteLine(prefix + (exported ? "export " : string.Empty) + "interface " + type.AssignedName + " {");
|
||||||
|
foreach (var field in type.Fields)
|
||||||
|
{
|
||||||
|
var shouldDefineNamespace = type.IsRoot && config.SecondaryNamespace != null && config.Namespace != null && (field.Type.Type == JsonTypeEnum.Object || (field.Type.InternalType != null && field.Type.InternalType.Type == JsonTypeEnum.Object));
|
||||||
|
if (config.ExamplesInDocumentation)
|
||||||
|
{
|
||||||
|
sw.WriteLine();
|
||||||
|
sw.WriteLine(prefix + " /**");
|
||||||
|
sw.WriteLine(prefix + " * Examples: " + field.GetExamplesText());
|
||||||
|
sw.WriteLine(prefix + " */");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sw.WriteLine(prefix + " " + field.JsonMemberName + (IsNullable(field.Type.Type) ? "?" : "") + ": " + (shouldDefineNamespace ? config.SecondaryNamespace + "." : string.Empty) + GetTypeName(field.Type, config) + ";");
|
||||||
|
}
|
||||||
|
sw.WriteLine(prefix + "}");
|
||||||
|
sw.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsNullable(JsonTypeEnum type)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
type == JsonTypeEnum.NullableBoolean ||
|
||||||
|
type == JsonTypeEnum.NullableDate ||
|
||||||
|
type == JsonTypeEnum.NullableFloat ||
|
||||||
|
type == JsonTypeEnum.NullableInteger ||
|
||||||
|
type == JsonTypeEnum.NullableLong ||
|
||||||
|
type == JsonTypeEnum.NullableSomething;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteFileStart(IJsonClassGeneratorConfig config, TextWriter sw)
|
||||||
|
{
|
||||||
|
foreach (var line in JsonClassGenerator.FileHeader)
|
||||||
|
{
|
||||||
|
sw.WriteLine("// " + line);
|
||||||
|
}
|
||||||
|
sw.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteFileEnd(IJsonClassGeneratorConfig config, TextWriter sw)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetNamespace(IJsonClassGeneratorConfig config, bool root)
|
||||||
|
{
|
||||||
|
return root ? config.Namespace : (config.SecondaryNamespace ?? config.Namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteNamespaceStart(IJsonClassGeneratorConfig config, TextWriter sw, bool root)
|
||||||
|
{
|
||||||
|
if (GetNamespace(config, root) != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
sw.WriteLine("module " + GetNamespace(config, root) + " {");
|
||||||
|
sw.WriteLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteNamespaceEnd(IJsonClassGeneratorConfig config, TextWriter sw, bool root)
|
||||||
|
{
|
||||||
|
if (GetNamespace(config, root) != null)
|
||||||
|
{
|
||||||
|
sw.WriteLine("}");
|
||||||
|
sw.WriteLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
170
Json2CSharpCodeGenerator.Lib/CodeWriters/vb.txt
Normal file
170
Json2CSharpCodeGenerator.Lib/CodeWriters/vb.txt
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Xamasoft.JsonClassGenerator.CodeWriters
|
||||||
|
{
|
||||||
|
public class VisualBasicCodeWriter : ICodeWriter
|
||||||
|
{
|
||||||
|
public string FileExtension
|
||||||
|
{
|
||||||
|
get { return ".vb"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string DisplayName
|
||||||
|
{
|
||||||
|
get { return "Visual Basic .NET"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private const string NoRenameAttribute = "<Obfuscation(Feature:=\"renaming\", Exclude:=true)>";
|
||||||
|
private const string NoPruneAttribute = "<Obfuscation(Feature:=\"trigger\", Exclude:=false)>";
|
||||||
|
|
||||||
|
public string GetTypeName(JsonType type, IJsonClassGeneratorConfig config)
|
||||||
|
{
|
||||||
|
var arraysAsLists = config.ExplicitDeserialization;
|
||||||
|
|
||||||
|
switch (type.Type)
|
||||||
|
{
|
||||||
|
case JsonTypeEnum.Anything: return "Object";
|
||||||
|
case JsonTypeEnum.Array: return arraysAsLists ? "IList(Of " + GetTypeName(type.InternalType, config) + ")" : GetTypeName(type.InternalType, config) + "()";
|
||||||
|
case JsonTypeEnum.Dictionary: return "Dictionary(Of String, " + GetTypeName(type.InternalType, config) + ")";
|
||||||
|
case JsonTypeEnum.Boolean: return "Boolean";
|
||||||
|
case JsonTypeEnum.Float: return "Double";
|
||||||
|
case JsonTypeEnum.Integer: return "Integer";
|
||||||
|
case JsonTypeEnum.Long: return "Long";
|
||||||
|
case JsonTypeEnum.Date: return "DateTime";
|
||||||
|
case JsonTypeEnum.NonConstrained: return "Object";
|
||||||
|
case JsonTypeEnum.NullableBoolean: return "Boolean?";
|
||||||
|
case JsonTypeEnum.NullableFloat: return "Double?";
|
||||||
|
case JsonTypeEnum.NullableInteger: return "Integer?";
|
||||||
|
case JsonTypeEnum.NullableLong: return "Long?";
|
||||||
|
case JsonTypeEnum.NullableDate: return "DateTime?";
|
||||||
|
case JsonTypeEnum.NullableSomething: return "Object";
|
||||||
|
case JsonTypeEnum.Object: return type.AssignedName;
|
||||||
|
case JsonTypeEnum.String: return "String";
|
||||||
|
default: throw new System.NotSupportedException("Unsupported json type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ShouldApplyNoRenamingAttribute(IJsonClassGeneratorConfig config)
|
||||||
|
{
|
||||||
|
return config.ApplyObfuscationAttributes && !config.ExplicitDeserialization && !config.UsePascalCase;
|
||||||
|
}
|
||||||
|
private bool ShouldApplyNoPruneAttribute(IJsonClassGeneratorConfig config)
|
||||||
|
{
|
||||||
|
return config.ApplyObfuscationAttributes && !config.ExplicitDeserialization && config.UseProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteClass(IJsonClassGeneratorConfig config, TextWriter sw, JsonType type)
|
||||||
|
{
|
||||||
|
var visibility = config.InternalVisibility ? "Friend" : "Public";
|
||||||
|
|
||||||
|
if (config.UseNestedClasses)
|
||||||
|
{
|
||||||
|
sw.WriteLine(" {0} Partial Class {1}", visibility, config.MainClass);
|
||||||
|
if (!type.IsRoot)
|
||||||
|
{
|
||||||
|
if (ShouldApplyNoRenamingAttribute(config)) sw.WriteLine(" " + NoRenameAttribute);
|
||||||
|
if (ShouldApplyNoPruneAttribute(config)) sw.WriteLine(" " + NoPruneAttribute);
|
||||||
|
sw.WriteLine(" {0} Class {1}", visibility, type.AssignedName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ShouldApplyNoRenamingAttribute(config)) sw.WriteLine(" " + NoRenameAttribute);
|
||||||
|
if (ShouldApplyNoPruneAttribute(config)) sw.WriteLine(" " + NoPruneAttribute);
|
||||||
|
sw.WriteLine(" {0} Class {1}", visibility, type.AssignedName);
|
||||||
|
}
|
||||||
|
|
||||||
|
var prefix = config.UseNestedClasses && !type.IsRoot ? " " : " ";
|
||||||
|
|
||||||
|
WriteClassMembers(config, sw, type, prefix);
|
||||||
|
|
||||||
|
if (config.UseNestedClasses && !type.IsRoot)
|
||||||
|
sw.WriteLine(" End Class");
|
||||||
|
|
||||||
|
sw.WriteLine(" End Class");
|
||||||
|
sw.WriteLine();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void WriteClassMembers(IJsonClassGeneratorConfig config, TextWriter sw, JsonType type, string prefix)
|
||||||
|
{
|
||||||
|
foreach (var field in type.Fields)
|
||||||
|
{
|
||||||
|
if (config.UsePascalCase || config.ExamplesInDocumentation) sw.WriteLine();
|
||||||
|
|
||||||
|
if (config.ExamplesInDocumentation)
|
||||||
|
{
|
||||||
|
sw.WriteLine(prefix + "''' <summary>");
|
||||||
|
sw.WriteLine(prefix + "''' Examples: " + field.GetExamplesText());
|
||||||
|
sw.WriteLine(prefix + "''' </summary>");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (config.UsePascalCase)
|
||||||
|
{
|
||||||
|
sw.WriteLine(prefix + "<JsonProperty(\"{0}\")>", field.JsonMemberName);
|
||||||
|
}
|
||||||
|
|
||||||
|
var validVbName = VisualBasicReservedWords.IsReserved(field.MemberName) ? $"[{field.MemberName}]" : field.MemberName;
|
||||||
|
|
||||||
|
if (config.UseProperties)
|
||||||
|
{
|
||||||
|
sw.WriteLine(prefix + "Public Property {1} As {0}", field.Type.GetTypeName(), validVbName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sw.WriteLine(prefix + "Public {1} As {0}", field.Type.GetTypeName(), validVbName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void WriteFileStart(IJsonClassGeneratorConfig config, TextWriter sw)
|
||||||
|
{
|
||||||
|
foreach (var line in JsonClassGenerator.FileHeader)
|
||||||
|
{
|
||||||
|
sw.WriteLine("' " + line);
|
||||||
|
}
|
||||||
|
sw.WriteLine();
|
||||||
|
sw.WriteLine("Imports System");
|
||||||
|
sw.WriteLine("Imports System.Collections.Generic");
|
||||||
|
if (ShouldApplyNoRenamingAttribute(config) || ShouldApplyNoPruneAttribute(config))
|
||||||
|
sw.WriteLine("Imports System.Reflection");
|
||||||
|
if (config.UsePascalCase)
|
||||||
|
sw.WriteLine("Imports Newtonsoft.Json");
|
||||||
|
sw.WriteLine("Imports Newtonsoft.Json.Linq");
|
||||||
|
if (config.SecondaryNamespace != null && config.HasSecondaryClasses && !config.UseNestedClasses)
|
||||||
|
{
|
||||||
|
sw.WriteLine("Imports {0}", config.SecondaryNamespace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteFileEnd(IJsonClassGeneratorConfig config, TextWriter sw)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void WriteNamespaceStart(IJsonClassGeneratorConfig config, TextWriter sw, bool root)
|
||||||
|
{
|
||||||
|
sw.WriteLine();
|
||||||
|
sw.WriteLine("Namespace Global.{0}", root && !config.UseNestedClasses ? config.Namespace : (config.SecondaryNamespace ?? config.Namespace));
|
||||||
|
sw.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteNamespaceEnd(IJsonClassGeneratorConfig config, TextWriter sw, bool root)
|
||||||
|
{
|
||||||
|
|
||||||
|
sw.WriteLine("End Namespace");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
128
Json2CSharpCodeGenerator.Lib/FieldInfo.cs
Normal file
128
Json2CSharpCodeGenerator.Lib/FieldInfo.cs
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
// Copyright © 2010 Xamasoft
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Json2CSharpCodeGenerator.Lib;
|
||||||
|
|
||||||
|
public class FieldInfo
|
||||||
|
{
|
||||||
|
public FieldInfo(
|
||||||
|
IJsonClassGeneratorConfig generator,
|
||||||
|
string jsonMemberName,
|
||||||
|
JsonType type,
|
||||||
|
bool usePascalCase,
|
||||||
|
IReadOnlyList<object> examples
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this._Generator = generator ?? throw new ArgumentNullException(nameof(generator));
|
||||||
|
JsonMemberName = jsonMemberName ?? throw new ArgumentNullException(nameof(jsonMemberName));
|
||||||
|
MemberName = jsonMemberName;
|
||||||
|
ContainsSpecialChars = IsContainsSpecialChars(MemberName);
|
||||||
|
|
||||||
|
if (usePascalCase || ContainsSpecialChars)
|
||||||
|
{
|
||||||
|
MemberName = JsonClassGenerator.ToTitleCase(MemberName);
|
||||||
|
}
|
||||||
|
|
||||||
|
Type = type ?? throw new ArgumentNullException(nameof(type));
|
||||||
|
Examples = examples ?? Array.Empty<object>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly IJsonClassGeneratorConfig _Generator;
|
||||||
|
|
||||||
|
/// <summary>Pascal-cased.</summary>
|
||||||
|
public string MemberName { get; }
|
||||||
|
/// <summary>Normally camelCased.</summary>
|
||||||
|
public string JsonMemberName { get; }
|
||||||
|
public JsonType Type { get; }
|
||||||
|
public IReadOnlyList<object> Examples { get; }
|
||||||
|
|
||||||
|
public bool ContainsSpecialChars { get; set; }
|
||||||
|
public int MyProperty { get; set; }
|
||||||
|
|
||||||
|
private static bool IsContainsSpecialChars(string text)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < text.Length; i++)
|
||||||
|
{
|
||||||
|
char c = text[i];
|
||||||
|
if (!char.IsLetterOrDigit(c) && c != '_')
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string ToTitleCase(string text)
|
||||||
|
{
|
||||||
|
if (text is null)
|
||||||
|
throw new ArgumentNullException(nameof(text));
|
||||||
|
if (string.IsNullOrWhiteSpace(text))
|
||||||
|
return text;
|
||||||
|
|
||||||
|
StringBuilder sb = new(text.Length);
|
||||||
|
bool lastCharWasSpecial = true;
|
||||||
|
|
||||||
|
for (int i = 0; i < text.Length; i++)
|
||||||
|
{
|
||||||
|
char c = text[i];
|
||||||
|
if (char.IsLetterOrDigit(c))
|
||||||
|
{
|
||||||
|
_ = sb.Append(lastCharWasSpecial ? char.ToUpper(c) : c);
|
||||||
|
lastCharWasSpecial = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lastCharWasSpecial = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetGenerationCode(string jObject)
|
||||||
|
{
|
||||||
|
if (jObject is null)
|
||||||
|
throw new ArgumentNullException(nameof(jObject));
|
||||||
|
|
||||||
|
if (Type.Type == JsonTypeEnum.Array)
|
||||||
|
{
|
||||||
|
JsonType innermost = Type.GetInnermostType();
|
||||||
|
return string.Format(CultureInfo.InvariantCulture, "({1})JsonClassHelper.ReadArray<{5}>(JsonClassHelper.GetJToken<JArray>({0}, \"{2}\"), JsonClassHelper.{3}, typeof({6}))",
|
||||||
|
jObject,
|
||||||
|
Type.GetTypeName(),
|
||||||
|
JsonMemberName,
|
||||||
|
innermost.GetReaderName(),
|
||||||
|
-1,
|
||||||
|
innermost.GetTypeName(),
|
||||||
|
Type.GetTypeName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if (Type.Type == JsonTypeEnum.Dictionary)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.InvariantCulture, "({1})JsonClassHelper.ReadDictionary<{2}>(JsonClassHelper.GetJToken<JObject>({0}, \"{3}\"))",
|
||||||
|
jObject,
|
||||||
|
Type.GetTypeName(),
|
||||||
|
Type.InternalType.GetTypeName(),
|
||||||
|
JsonMemberName,
|
||||||
|
Type.GetTypeName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.InvariantCulture, "JsonClassHelper.{1}(JsonClassHelper.GetJToken<{2}>({0}, \"{3}\"))",
|
||||||
|
jObject,
|
||||||
|
Type.GetReaderName(),
|
||||||
|
Type.GetJTokenType(),
|
||||||
|
JsonMemberName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetExamplesText() => string.Join(separator: ", ", values: Examples.Take(5).Select(x => JsonConvert.SerializeObject(x)));
|
||||||
|
|
||||||
|
}
|
17
Json2CSharpCodeGenerator.Lib/Helpers.cs
Normal file
17
Json2CSharpCodeGenerator.Lib/Helpers.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace Json2CSharpCodeGenerator.Lib;
|
||||||
|
|
||||||
|
public static class StringExtension
|
||||||
|
{
|
||||||
|
public static string ToCamelCase(this string str)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(str) && str.Length > 1)
|
||||||
|
{
|
||||||
|
return char.ToLowerInvariant(str[0]) + str.Substring(1);
|
||||||
|
}
|
||||||
|
return str.ToLowerInvariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string RemoveSpecialCharacters(this string str) => Regex.Replace(str, "[^a-zA-Z0-9]+", "", RegexOptions.Compiled);
|
||||||
|
}
|
34
Json2CSharpCodeGenerator.Lib/ICodeWriter.cs
Normal file
34
Json2CSharpCodeGenerator.Lib/ICodeWriter.cs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Json2CSharpCodeGenerator.Lib;
|
||||||
|
|
||||||
|
public interface ICodeBuilder
|
||||||
|
{
|
||||||
|
string FileExtension { get; }
|
||||||
|
string DisplayName { get; }
|
||||||
|
|
||||||
|
string GetTypeName(JsonType type, IJsonClassGeneratorConfig config);
|
||||||
|
|
||||||
|
void WriteClass(IJsonClassGeneratorConfig config, StringBuilder sw, JsonType type);
|
||||||
|
void WriteFileStart(IJsonClassGeneratorConfig config, StringBuilder sw);
|
||||||
|
void WriteFileEnd(IJsonClassGeneratorConfig config, StringBuilder sw);
|
||||||
|
void WriteNamespaceStart(IJsonClassGeneratorConfig config, StringBuilder sw, bool root);
|
||||||
|
void WriteNamespaceEnd(IJsonClassGeneratorConfig config, StringBuilder sw, bool root);
|
||||||
|
void WriteDeserializationComment(IJsonClassGeneratorConfig config, StringBuilder sw, bool rootIsArray = false);
|
||||||
|
void WriteClassMembers(IJsonClassGeneratorConfig config, StringBuilder sw, JsonType type, string prefix);
|
||||||
|
|
||||||
|
IReadOnlyCollection<string> ReservedKeywords { get; }
|
||||||
|
bool IsReservedKeyword(string word);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ICodeWriter
|
||||||
|
{
|
||||||
|
string FileExtension { get; }
|
||||||
|
string DisplayName { get; }
|
||||||
|
string GetTypeName(JsonType type, IJsonClassGeneratorConfig config);
|
||||||
|
void WriteClass(IJsonClassGeneratorConfig config, TextWriter sw, JsonType type);
|
||||||
|
void WriteFileStart(IJsonClassGeneratorConfig config, TextWriter sw);
|
||||||
|
void WriteFileEnd(IJsonClassGeneratorConfig config, TextWriter sw);
|
||||||
|
void WriteNamespaceStart(IJsonClassGeneratorConfig config, TextWriter sw, bool root);
|
||||||
|
void WriteNamespaceEnd(IJsonClassGeneratorConfig config, TextWriter sw, bool root);
|
||||||
|
}
|
192
Json2CSharpCodeGenerator.Lib/IJsonClassGeneratorConfig.cs
Normal file
192
Json2CSharpCodeGenerator.Lib/IJsonClassGeneratorConfig.cs
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
namespace Json2CSharpCodeGenerator.Lib;
|
||||||
|
|
||||||
|
public interface IJsonClassGeneratorConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The C# <c>namespace</c> or Java <c>package</c> that the generated types will reside in.<br />
|
||||||
|
/// <see langword="null"/> by default. If null/empty/whitespace then no enclosing namespace will be written in the output.
|
||||||
|
/// </summary>
|
||||||
|
string Namespace { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The C# <c>namespace</c> or Java <c>package</c> that "secondary" generated types will reside in.<br />
|
||||||
|
/// <see langword="null"/> by default.
|
||||||
|
/// </summary>
|
||||||
|
string SecondaryNamespace { get; set; }
|
||||||
|
|
||||||
|
OutputTypes OutputType { get; set; }
|
||||||
|
OutputCollectionType CollectionType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Options contained within are only respected when <see cref="OutputType"/> == <see cref="OutputTypes.MutableClass"/>.</summary>
|
||||||
|
MutableClassConfig MutableClasses { get; }
|
||||||
|
|
||||||
|
JsonLibrary AttributeLibrary { get; set; }
|
||||||
|
JsonPropertyAttributeUsage AttributeUsage { get; set; }
|
||||||
|
|
||||||
|
bool InternalVisibility { get; set; }
|
||||||
|
bool NoHelperClass { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When true, then the generated C# and VB.NET classes will have PascalCase property names, which means that <c>[JsonProperty]</c> or <c>[JsonPropertyName]</c> will be applied to all properties if the source JSON uses camelCase names, regardless of <see cref="AttributeUsage"/>.<br />
|
||||||
|
/// When false, then the generated C# and VB.NET classes' property names will use the same names as the source JSON (except when a JSON property name cannot be represented by a C# identifier).
|
||||||
|
/// </summary>
|
||||||
|
bool UsePascalCase { get; set; }
|
||||||
|
|
||||||
|
bool UseNestedClasses { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Name of the outer class. Only used when <c><see cref="UseNestedClasses"/> == <see langword="true"/></c>.</summary>
|
||||||
|
string MainClass { get; set; }
|
||||||
|
|
||||||
|
/// <summary>When <see langword="true"/>, then <see cref="System.Reflection.ObfuscationAttribute"/> will be applied to generated types.</summary>
|
||||||
|
bool ApplyObfuscationAttributes { get; set; }
|
||||||
|
|
||||||
|
bool SingleFile { get; set; }
|
||||||
|
ICodeBuilder CodeWriter { get; set; }
|
||||||
|
bool AlwaysUseNullableValues { get; set; }
|
||||||
|
bool ExamplesInDocumentation { get; set; }
|
||||||
|
bool RemoveToJson { get; set; }
|
||||||
|
bool RemoveFromJson { get; set; }
|
||||||
|
bool RemoveConstructors { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum OutputTypes
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// C#: Mutable <c>class</c> types.<br />
|
||||||
|
/// VB.NET: Mutable <c>Class</c> types.<br />
|
||||||
|
/// Java: Mutable Bean <c>class</c>
|
||||||
|
/// </summary>
|
||||||
|
MutableClass,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// C#: Immutable <c>class</c> types. Using <c>[JsonConstructor]</c>.<br />
|
||||||
|
/// VB.NET: Immutable <c>Class</c> types. Using <c>[JsonConstructor]</c>.<br />
|
||||||
|
/// Java: Not yet implemented. TODO.
|
||||||
|
/// </summary>
|
||||||
|
ImmutableClass,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// C#: Immutable <c>record</c> types.<br />
|
||||||
|
/// VB.NET: Not supported.<br />
|
||||||
|
/// Java: Not supported.<br />
|
||||||
|
/// </summary>
|
||||||
|
ImmutableRecord
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum OutputCollectionType
|
||||||
|
{
|
||||||
|
/// <summary>Expose collections as <c>T[]</c>.</summary>
|
||||||
|
Array,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// C#/VB.NET: Expose collections as <c><see cref="List{T}"/></c>.<br />
|
||||||
|
/// Java: Uses <c>ArrayList<T></c>
|
||||||
|
/// </summary>
|
||||||
|
MutableList,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// C#/VB.NET: Expose collections as <c><see cref="IReadOnlyList{T}"/></c>.<br />
|
||||||
|
/// Java: Not supported.
|
||||||
|
/// </summary>
|
||||||
|
IReadOnlyList,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// C#/VB.NET: Expose collections as <c>System.Collections.Immutable.ImmutableArray<T></c>.<br />
|
||||||
|
/// Java: Not supported.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks><c>ImmutableArray</c> is preferred over <c>ImmutableList</c> when append functionality isn't required.</remarks>
|
||||||
|
ImmutableArray
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum OutputMembers
|
||||||
|
{
|
||||||
|
/// <summary>C# and VB.NET: Uses auto-properties. Java: uses getter/setter methods.</summary>
|
||||||
|
AsProperties,
|
||||||
|
AsPublicFields,
|
||||||
|
// AsPublicPropertiesOverPrivateFields // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum JsonLibrary
|
||||||
|
{
|
||||||
|
/// <summary>Use the <see cref="Newtonsoft.Json.JsonPropertyAttribute"/> on generated C# class properties.</summary>
|
||||||
|
NewtonsoftJson,
|
||||||
|
|
||||||
|
/// <summary>Use the <c>[JsonPropertyName]</c> attribute on generated C# class properties.</summary>
|
||||||
|
SystemTextJson
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum JsonPropertyAttributeUsage
|
||||||
|
{
|
||||||
|
/// <summary>The <c>[JsonProperty]</c> or <c>[JsonPropertyName]</c> attributes will be applied to all properties.</summary>
|
||||||
|
Always,
|
||||||
|
|
||||||
|
/// <summary>The <c>[JsonProperty]</c> or <c>[JsonPropertyName]</c> attributes will only be applied to properties with names that cannot be expressed as C# identifiers.</summary>
|
||||||
|
OnlyWhenNecessary
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MutableClassConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// When <see langword="true"/>, then all properties for collections are read-only, though the actual collection-type can still be mutable. e.g. <c>public List<String> StringValues { get; }</c><br />
|
||||||
|
/// When <see langword="false"/>, then all properties for collections are read-only, though the actual collection-type can still be mutable. e.g. <c>public List<String> StringValues { get; set; }</c><br />
|
||||||
|
/// Default is <see langword="false"/>.
|
||||||
|
/// </summary>
|
||||||
|
public bool ReadOnlyCollectionProperties { get; set; }
|
||||||
|
|
||||||
|
public OutputMembers Members { get; set; } = OutputMembers.AsProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class JsonClassGeneratorConfigExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Never returns <see langword="null"/>. Returns either:
|
||||||
|
/// <list type="bullet">
|
||||||
|
/// <item>"<c>[JsonPropertyName("<paramref name="field"/>.<see cref="FieldInfo.JsonMemberName"/>")]</c>" (for <c>System.Text.Json</c>).</item>
|
||||||
|
/// <item>"<c>[JsonProperty("<paramref name="field"/>.<see cref="FieldInfo.JsonMemberName"/>")]</c>" (for <c>Newtonsoft.Json</c>).</item>
|
||||||
|
/// <item>"<c>[property: JsonPropertyName("<paramref name="field"/>.<see cref="FieldInfo.JsonMemberName"/>")]</c>" (for <c>System.Text.Json</c>) when <see cref="IJsonClassGeneratorConfig.RecordTypes"/> is <see langword="true"/>.</item>
|
||||||
|
/// <item>"<c>[property: JsonProperty("<paramref name="field"/>.<see cref="FieldInfo.JsonMemberName"/>")]</c>" (for <c>Newtonsoft.Json</c>) when <see cref="IJsonClassGeneratorConfig.RecordTypes"/> is <see langword="true"/>.</item>
|
||||||
|
/// <item>An empty string depending on <paramref name="config"/> and <see cref="FieldInfo.ContainsSpecialChars"/>.</item>
|
||||||
|
/// </list>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">Required. Cannot be <see langword="null"/>.</param>
|
||||||
|
/// <param name="field">Required. Cannot be <see langword="null"/>.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetCSharpJsonAttributeCode(this IJsonClassGeneratorConfig config, FieldInfo field)
|
||||||
|
{
|
||||||
|
if (config is null)
|
||||||
|
throw new ArgumentNullException(nameof(config));
|
||||||
|
if (field is null)
|
||||||
|
throw new ArgumentNullException(nameof(field));
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
if (UsePropertyAttribute(config, field))
|
||||||
|
{
|
||||||
|
bool usingRecordTypes = config.OutputType == OutputTypes.ImmutableRecord;
|
||||||
|
string attributeTarget = usingRecordTypes ? "property: " : string.Empty;
|
||||||
|
|
||||||
|
return config.AttributeLibrary switch
|
||||||
|
{
|
||||||
|
JsonLibrary.NewtonsoftJson => $"[{attributeTarget}JsonProperty(\"{field.JsonMemberName}\")]",
|
||||||
|
JsonLibrary.SystemTextJson => $"[{attributeTarget}JsonPropertyName(\"{field.JsonMemberName}\")]",
|
||||||
|
_ => throw new InvalidOperationException("Unrecognized " + nameof(config.AttributeLibrary) + " value: " + config.AttributeLibrary),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool UsePropertyAttribute(IJsonClassGeneratorConfig config, FieldInfo field)
|
||||||
|
{
|
||||||
|
return config.AttributeUsage switch
|
||||||
|
{
|
||||||
|
JsonPropertyAttributeUsage.Always => true,
|
||||||
|
JsonPropertyAttributeUsage.OnlyWhenNecessary => field.ContainsSpecialChars,
|
||||||
|
_ => throw new InvalidOperationException("Unrecognized " + nameof(config.AttributeUsage) + " value: " + config.AttributeUsage),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool HasNamespace(this IJsonClassGeneratorConfig config) => !string.IsNullOrEmpty(config.Namespace);
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<LangVersion>10.0</LangVersion>
|
||||||
|
<Nullable>disable</Nullable>
|
||||||
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="CG.Pluralization" Version="0.3000.12" />
|
||||||
|
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
364
Json2CSharpCodeGenerator.Lib/JsonClassGenerator.cs
Normal file
364
Json2CSharpCodeGenerator.Lib/JsonClassGenerator.cs
Normal file
@ -0,0 +1,364 @@
|
|||||||
|
// Copyright © 2010 Xamasoft
|
||||||
|
|
||||||
|
using Humanizer;
|
||||||
|
using Json2CSharpCodeGenerator.Lib.CodeWriters;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Json2CSharpCodeGenerator.Lib;
|
||||||
|
|
||||||
|
public class JsonClassGenerator : IJsonClassGeneratorConfig
|
||||||
|
{
|
||||||
|
#region IJsonClassGeneratorConfig
|
||||||
|
|
||||||
|
public OutputTypes OutputType { get; set; } = OutputTypes.MutableClass;
|
||||||
|
public OutputCollectionType CollectionType { get; set; } = OutputCollectionType.MutableList;
|
||||||
|
public MutableClassConfig MutableClasses { get; } = new MutableClassConfig();
|
||||||
|
public JsonLibrary AttributeLibrary { get; set; } = JsonLibrary.NewtonsoftJson;
|
||||||
|
public JsonPropertyAttributeUsage AttributeUsage { get; set; } = JsonPropertyAttributeUsage.OnlyWhenNecessary;
|
||||||
|
|
||||||
|
public string Namespace { get; set; }
|
||||||
|
public string SecondaryNamespace { get; set; }
|
||||||
|
|
||||||
|
public bool InternalVisibility { get; set; }
|
||||||
|
public bool NoHelperClass { get; set; }
|
||||||
|
public string MainClass { get; set; }
|
||||||
|
public bool UsePascalCase { get; set; }
|
||||||
|
public bool UseNestedClasses { get; set; }
|
||||||
|
public bool ApplyObfuscationAttributes { get; set; }
|
||||||
|
public bool SingleFile { get; set; }
|
||||||
|
public ICodeBuilder CodeWriter { get; set; }
|
||||||
|
public bool AlwaysUseNullableValues { get; set; }
|
||||||
|
public bool ExamplesInDocumentation { get; set; }
|
||||||
|
public bool RemoveToJson { get; set; }
|
||||||
|
public bool RemoveFromJson { get; set; }
|
||||||
|
public bool RemoveConstructors { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public TextWriter OutputStream { get; set; }
|
||||||
|
|
||||||
|
//private readonly PluralizationService pluralizationService = PluralizationService.CreateService(new CultureInfo("en-US"));
|
||||||
|
|
||||||
|
public StringBuilder GenerateClasses(string jsonInput, out string errorMessage)
|
||||||
|
{
|
||||||
|
JObject[] examples = null;
|
||||||
|
bool rootWasArray = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using StringReader sr = new(jsonInput);
|
||||||
|
using JsonTextReader reader = new(sr);
|
||||||
|
JToken json = JToken.ReadFrom(reader);
|
||||||
|
if (json is JArray jArray && (jArray.Count == 0 || jArray.All(el => el is JObject)))
|
||||||
|
{
|
||||||
|
rootWasArray = true;
|
||||||
|
examples = jArray.Cast<JObject>().ToArray();
|
||||||
|
}
|
||||||
|
else if (json is JObject jObject)
|
||||||
|
{
|
||||||
|
examples = new[] { jObject };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
errorMessage = "Exception: " + ex.Message;
|
||||||
|
return new StringBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (CodeWriter == null)
|
||||||
|
CodeWriter = new CSharpCodeWriter();
|
||||||
|
|
||||||
|
Types = new List<JsonType>();
|
||||||
|
_ = this._Names.Add("Root");
|
||||||
|
JsonType rootType = new(this, examples[0])
|
||||||
|
{
|
||||||
|
IsRoot = true
|
||||||
|
};
|
||||||
|
rootType.AssignName("Root");
|
||||||
|
GenerateClass(examples, rootType);
|
||||||
|
|
||||||
|
Types = HandleDuplicateClasses(Types);
|
||||||
|
|
||||||
|
StringBuilder builder = new();
|
||||||
|
WriteClassesToFile(builder, Types, rootWasArray);
|
||||||
|
|
||||||
|
errorMessage = string.Empty;
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
errorMessage = ex.ToString();
|
||||||
|
return new StringBuilder();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteClassesToFile(StringBuilder sw, IEnumerable<JsonType> types, bool rootIsArray = false)
|
||||||
|
{
|
||||||
|
bool inNamespace = false;
|
||||||
|
bool rootNamespace = false;
|
||||||
|
|
||||||
|
CodeWriter.WriteFileStart(this, sw);
|
||||||
|
CodeWriter.WriteDeserializationComment(this, sw, rootIsArray);
|
||||||
|
|
||||||
|
foreach (JsonType type in types)
|
||||||
|
{
|
||||||
|
if (this.HasNamespace() && inNamespace && rootNamespace != type.IsRoot && SecondaryNamespace != null)
|
||||||
|
{
|
||||||
|
CodeWriter.WriteNamespaceEnd(this, sw, rootNamespace);
|
||||||
|
inNamespace = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.HasNamespace() && !inNamespace)
|
||||||
|
{
|
||||||
|
CodeWriter.WriteNamespaceStart(this, sw, type.IsRoot);
|
||||||
|
inNamespace = true;
|
||||||
|
rootNamespace = type.IsRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeWriter.WriteClass(this, sw, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.HasNamespace() && inNamespace)
|
||||||
|
{
|
||||||
|
CodeWriter.WriteNamespaceEnd(this, sw, rootNamespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeWriter.WriteFileEnd(this, sw);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GenerateClass(JObject[] examples, JsonType type)
|
||||||
|
{
|
||||||
|
Dictionary<string, JsonType> jsonFields = new();
|
||||||
|
Dictionary<string, List<object>> fieldExamples = new();
|
||||||
|
|
||||||
|
bool first = true;
|
||||||
|
|
||||||
|
foreach (JObject obj in examples)
|
||||||
|
{
|
||||||
|
foreach (JProperty prop in obj.Properties())
|
||||||
|
{
|
||||||
|
JsonType fieldType;
|
||||||
|
JsonType currentType = new(this, prop.Value);
|
||||||
|
string propName = prop.Name;
|
||||||
|
|
||||||
|
if (jsonFields.TryGetValue(propName, out fieldType))
|
||||||
|
{
|
||||||
|
JsonType commonType = fieldType.GetCommonType(currentType);
|
||||||
|
|
||||||
|
jsonFields[propName] = commonType;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JsonType commonType = currentType;
|
||||||
|
if (first)
|
||||||
|
commonType = commonType.MaybeMakeNullable(this);
|
||||||
|
else
|
||||||
|
commonType = commonType.GetCommonType(JsonType.GetNull(this));
|
||||||
|
|
||||||
|
jsonFields.Add(propName, commonType);
|
||||||
|
fieldExamples[propName] = new List<object>();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<object> fe = fieldExamples[propName];
|
||||||
|
JToken val = prop.Value;
|
||||||
|
if (val.Type is JTokenType.Null or JTokenType.Undefined)
|
||||||
|
{
|
||||||
|
if (!fe.Contains(null))
|
||||||
|
{
|
||||||
|
fe.Insert(0, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
object v = val.Type is JTokenType.Array or JTokenType.Object ? val : val.Value<object>();
|
||||||
|
if (!fe.Any(x => v.Equals(x)))
|
||||||
|
{
|
||||||
|
fe.Add(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UseNestedClasses)
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<string, JsonType> field in jsonFields)
|
||||||
|
{
|
||||||
|
_ = this._Names.Add(field.Key.ToLower());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (KeyValuePair<string, JsonType> field in jsonFields)
|
||||||
|
{
|
||||||
|
JsonType fieldType = field.Value;
|
||||||
|
if (fieldType.Type == JsonTypeEnum.Object)
|
||||||
|
{
|
||||||
|
List<JObject> subexamples = new(examples.Length);
|
||||||
|
foreach (JObject obj in examples)
|
||||||
|
{
|
||||||
|
JToken value;
|
||||||
|
if (obj.TryGetValue(field.Key, out value))
|
||||||
|
{
|
||||||
|
if (value.Type == JTokenType.Object)
|
||||||
|
{
|
||||||
|
subexamples.Add((JObject)value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldType.AssignOriginalName(field.Key);
|
||||||
|
fieldType.AssignName(CreateUniqueClassName(field.Key));
|
||||||
|
fieldType.AssignNewAssignedName(ToTitleCase(field.Key));
|
||||||
|
|
||||||
|
GenerateClass(subexamples.ToArray(), fieldType);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fieldType.InternalType != null && fieldType.InternalType.Type == JsonTypeEnum.Object)
|
||||||
|
{
|
||||||
|
List<JObject> subexamples = new(examples.Length);
|
||||||
|
foreach (JObject obj in examples)
|
||||||
|
{
|
||||||
|
JToken value;
|
||||||
|
if (obj.TryGetValue(field.Key, out value))
|
||||||
|
{
|
||||||
|
if (value is JArray jArray)
|
||||||
|
{
|
||||||
|
const int MAX_JSON_ARRAY_ITEMS = 50; // Take like 30 items from the array this will increase the chance of getting all the objects accuralty while not analyzing all the data
|
||||||
|
|
||||||
|
subexamples.AddRange(jArray.OfType<JObject>().Take(MAX_JSON_ARRAY_ITEMS));
|
||||||
|
}
|
||||||
|
else if (value is JObject jObject) //TODO J2C : ONLY LOOP OVER 50 OBJECT AND NOT THE WHOLE THING
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<string, JToken> jsonObjectProperty in jObject)
|
||||||
|
{
|
||||||
|
// if (!(item.Value is JObject)) throw new NotSupportedException("Arrays of non-objects are not supported yet.");
|
||||||
|
if (jsonObjectProperty.Value is JObject innerObject)
|
||||||
|
{
|
||||||
|
subexamples.Add(innerObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
field.Value.InternalType.AssignOriginalName(field.Key);
|
||||||
|
field.Value.InternalType.AssignName(CreateUniqueClassNameFromPlural(field.Key));
|
||||||
|
field.Value.InternalType.AssignNewAssignedName(ToTitleCase(field.Key).Singularize(inputIsKnownToBePlural: false));
|
||||||
|
|
||||||
|
GenerateClass(subexamples.ToArray(), field.Value.InternalType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type.Fields = jsonFields
|
||||||
|
.Select(x => new FieldInfo(
|
||||||
|
generator: this,
|
||||||
|
jsonMemberName: x.Key,
|
||||||
|
type: x.Value,
|
||||||
|
usePascalCase: UsePascalCase || AttributeUsage == JsonPropertyAttributeUsage.Always,
|
||||||
|
examples: fieldExamples[x.Key])
|
||||||
|
)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(type.AssignedName))
|
||||||
|
{
|
||||||
|
Types.Add(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Checks if there are any duplicate classes in the input, and merges its corresponding properties (TEST CASE 7)</summary>
|
||||||
|
private IList<JsonType> HandleDuplicateClasses(IList<JsonType> types)
|
||||||
|
{
|
||||||
|
// TODO: This is currently O(n*n) because it iterates through List<T> on every loop iteration. This can be optimized.
|
||||||
|
|
||||||
|
List<JsonType> typesWithNoDuplicates = new();
|
||||||
|
types = types.OrderBy(p => p.AssignedName).ToList();
|
||||||
|
foreach (JsonType type in types)
|
||||||
|
{
|
||||||
|
if (!typesWithNoDuplicates.Exists(p => p.OriginalName == type.OriginalName))
|
||||||
|
{
|
||||||
|
typesWithNoDuplicates.Add(type);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JsonType duplicatedType = typesWithNoDuplicates.FirstOrDefault(p => p.OriginalName == type.OriginalName);
|
||||||
|
|
||||||
|
// Rename all references of this type to the original assigned name
|
||||||
|
foreach (FieldInfo field in type.Fields)
|
||||||
|
{
|
||||||
|
if (!duplicatedType.Fields.ToList().Exists(x => x.JsonMemberName == field.JsonMemberName))
|
||||||
|
{
|
||||||
|
duplicatedType.Fields.Add(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return typesWithNoDuplicates;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IList<JsonType> Types { get; private set; }
|
||||||
|
private readonly HashSet<string> _Names = new();
|
||||||
|
|
||||||
|
private string CreateUniqueClassName(string name)
|
||||||
|
{
|
||||||
|
name = ToTitleCase(name);
|
||||||
|
|
||||||
|
string finalName = name;
|
||||||
|
int i = 2;
|
||||||
|
while (this._Names.Any(x => x.Equals(finalName, StringComparison.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
|
||||||
|
finalName = name + i.ToString();
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = this._Names.Add(finalName);
|
||||||
|
return finalName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string CreateUniqueClassNameFromPlural(string plural)
|
||||||
|
{
|
||||||
|
plural = ToTitleCase(plural);
|
||||||
|
string singular = plural.Singularize(inputIsKnownToBePlural: false);
|
||||||
|
return CreateUniqueClassName(singular);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string ToTitleCase(string str)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new(str.Length);
|
||||||
|
bool flag = true;
|
||||||
|
|
||||||
|
for (int i = 0; i < str.Length; i++)
|
||||||
|
{
|
||||||
|
char c = str[i];
|
||||||
|
string specialCaseFirstCharIsNumber = string.Empty;
|
||||||
|
|
||||||
|
// Handle the case where the first character is a number
|
||||||
|
if (i == 0 && char.IsDigit(c))
|
||||||
|
specialCaseFirstCharIsNumber = "_" + c;
|
||||||
|
|
||||||
|
if (char.IsLetterOrDigit(c))
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(specialCaseFirstCharIsNumber))
|
||||||
|
{
|
||||||
|
_ = sb.Append(flag ? char.ToUpper(c) : c);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_ = sb.Append(flag ? specialCaseFirstCharIsNumber.ToUpper() : specialCaseFirstCharIsNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
flag = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
flag = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
203
Json2CSharpCodeGenerator.Lib/JsonClassHelper.cs
Normal file
203
Json2CSharpCodeGenerator.Lib/JsonClassHelper.cs
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
// JSON C# Class Generator
|
||||||
|
// http://www.xamasoft.com/json-class-generator
|
||||||
|
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace JsonCSharpClassGenerator;
|
||||||
|
|
||||||
|
internal static class JsonClassHelper
|
||||||
|
{
|
||||||
|
|
||||||
|
public static T GetJToken<T>(JObject obj, string field) where T : JToken
|
||||||
|
{
|
||||||
|
JToken value;
|
||||||
|
if (obj.TryGetValue(field, out value))
|
||||||
|
return GetJToken<T>(value);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static T GetJToken<T>(JToken token) where T : JToken
|
||||||
|
{
|
||||||
|
if (token == null)
|
||||||
|
return null;
|
||||||
|
if (token.Type == JTokenType.Null)
|
||||||
|
return null;
|
||||||
|
if (token.Type == JTokenType.Undefined)
|
||||||
|
return null;
|
||||||
|
return (T)token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ReadString(JToken token)
|
||||||
|
{
|
||||||
|
JValue value = GetJToken<JValue>(token);
|
||||||
|
if (value == null)
|
||||||
|
return null;
|
||||||
|
return (string)value.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ReadBoolean(JToken token)
|
||||||
|
{
|
||||||
|
JValue value = GetJToken<JValue>(token);
|
||||||
|
if (value == null)
|
||||||
|
throw new Newtonsoft.Json.JsonSerializationException();
|
||||||
|
return Convert.ToBoolean(value.Value);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool? ReadNullableBoolean(JToken token)
|
||||||
|
{
|
||||||
|
JValue value = GetJToken<JValue>(token);
|
||||||
|
if (value == null)
|
||||||
|
return null;
|
||||||
|
return Convert.ToBoolean(value.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int ReadInteger(JToken token)
|
||||||
|
{
|
||||||
|
JValue value = GetJToken<JValue>(token);
|
||||||
|
if (value == null)
|
||||||
|
throw new Newtonsoft.Json.JsonSerializationException();
|
||||||
|
return Convert.ToInt32((long)value.Value);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int? ReadNullableInteger(JToken token)
|
||||||
|
{
|
||||||
|
JValue value = GetJToken<JValue>(token);
|
||||||
|
if (value == null)
|
||||||
|
return null;
|
||||||
|
return Convert.ToInt32((long)value.Value);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long ReadLong(JToken token)
|
||||||
|
{
|
||||||
|
JValue value = GetJToken<JValue>(token);
|
||||||
|
if (value == null)
|
||||||
|
throw new Newtonsoft.Json.JsonSerializationException();
|
||||||
|
return Convert.ToInt64(value.Value);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long? ReadNullableLong(JToken token)
|
||||||
|
{
|
||||||
|
JValue value = GetJToken<JValue>(token);
|
||||||
|
if (value == null)
|
||||||
|
return null;
|
||||||
|
return Convert.ToInt64(value.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double ReadFloat(JToken token)
|
||||||
|
{
|
||||||
|
JValue value = GetJToken<JValue>(token);
|
||||||
|
if (value == null)
|
||||||
|
throw new Newtonsoft.Json.JsonSerializationException();
|
||||||
|
return Convert.ToDouble(value.Value);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double? ReadNullableFloat(JToken token)
|
||||||
|
{
|
||||||
|
JValue value = GetJToken<JValue>(token);
|
||||||
|
if (value == null)
|
||||||
|
return null;
|
||||||
|
return Convert.ToDouble(value.Value);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DateTime ReadDate(JToken token)
|
||||||
|
{
|
||||||
|
JValue value = GetJToken<JValue>(token);
|
||||||
|
if (value == null)
|
||||||
|
throw new Newtonsoft.Json.JsonSerializationException();
|
||||||
|
return Convert.ToDateTime(value.Value);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DateTime? ReadNullableDate(JToken token)
|
||||||
|
{
|
||||||
|
JValue value = GetJToken<JValue>(token);
|
||||||
|
if (value == null)
|
||||||
|
return null;
|
||||||
|
return Convert.ToDateTime(value.Value);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object ReadObject(JToken token)
|
||||||
|
{
|
||||||
|
JToken value = GetJToken<JToken>(token);
|
||||||
|
if (value == null)
|
||||||
|
return null;
|
||||||
|
if (value.Type == JTokenType.Object)
|
||||||
|
return value;
|
||||||
|
if (value.Type == JTokenType.Array)
|
||||||
|
return ReadArray(value, ReadObject);
|
||||||
|
|
||||||
|
if (value is JValue jvalue)
|
||||||
|
return jvalue.Value;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T ReadStronglyTypedObject<T>(JToken token) where T : class
|
||||||
|
{
|
||||||
|
JObject value = GetJToken<JObject>(token);
|
||||||
|
if (value == null)
|
||||||
|
return null;
|
||||||
|
return (T)Activator.CreateInstance(typeof(T), new object[] { token });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate T ValueReader<T>(JToken token);
|
||||||
|
|
||||||
|
public static T[] ReadArray<T>(JToken token, ValueReader<T> reader)
|
||||||
|
{
|
||||||
|
JArray value = GetJToken<JArray>(token);
|
||||||
|
if (value == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
T[] array = new T[value.Count];
|
||||||
|
for (int i = 0; i < array.Length; i++)
|
||||||
|
{
|
||||||
|
array[i] = reader(value[i]);
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dictionary<string, T> ReadDictionary<T>(JToken token)
|
||||||
|
{
|
||||||
|
JObject value = GetJToken<JObject>(token);
|
||||||
|
if (value == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Dictionary<string, T> dict = new();
|
||||||
|
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Array ReadArray<K>(JArray jArray, ValueReader<K> reader, Type type)
|
||||||
|
{
|
||||||
|
if (jArray == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Type elemType = type.GetElementType();
|
||||||
|
|
||||||
|
Array array = Array.CreateInstance(elemType, jArray.Count);
|
||||||
|
for (int i = 0; i < array.Length; i++)
|
||||||
|
{
|
||||||
|
if (elemType.IsArray)
|
||||||
|
{
|
||||||
|
array.SetValue(ReadArray(GetJToken<JArray>(jArray[i]), reader, elemType), i);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
array.SetValue(reader(jArray[i]), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
356
Json2CSharpCodeGenerator.Lib/JsonType.cs
Normal file
356
Json2CSharpCodeGenerator.Lib/JsonType.cs
Normal file
@ -0,0 +1,356 @@
|
|||||||
|
// Copyright © 2010 Xamasoft
|
||||||
|
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace Json2CSharpCodeGenerator.Lib;
|
||||||
|
|
||||||
|
public class JsonType
|
||||||
|
{
|
||||||
|
|
||||||
|
private JsonType(IJsonClassGeneratorConfig generator) => this._Generator = generator;
|
||||||
|
|
||||||
|
public JsonType(IJsonClassGeneratorConfig generator, JToken token)
|
||||||
|
: this(generator)
|
||||||
|
{
|
||||||
|
|
||||||
|
Type = GetFirstTypeEnum(token);
|
||||||
|
|
||||||
|
if (Type == JsonTypeEnum.Array)
|
||||||
|
{
|
||||||
|
JArray array = (JArray)token;
|
||||||
|
InternalType = GetCommonType(generator, array.ToArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static JsonType GetNull(IJsonClassGeneratorConfig generator) => new(generator, JsonTypeEnum.NullableSomething);
|
||||||
|
|
||||||
|
private readonly IJsonClassGeneratorConfig _Generator;
|
||||||
|
|
||||||
|
internal JsonType(IJsonClassGeneratorConfig generator, JsonTypeEnum type)
|
||||||
|
: this(generator) => Type = type;
|
||||||
|
|
||||||
|
public static JsonType GetCommonType(IJsonClassGeneratorConfig generator, JToken[] tokens)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (tokens.Length == 0)
|
||||||
|
return new JsonType(generator, JsonTypeEnum.NonConstrained);
|
||||||
|
|
||||||
|
JsonType common = new JsonType(generator, tokens[0]).MaybeMakeNullable(generator);
|
||||||
|
|
||||||
|
for (int i = 1; i < tokens.Length; i++)
|
||||||
|
{
|
||||||
|
JsonType current = new(generator, tokens[i]);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
common = common.GetCommonType(current);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return common;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
internal JsonType MaybeMakeNullable(IJsonClassGeneratorConfig generator)
|
||||||
|
{
|
||||||
|
if (!generator.AlwaysUseNullableValues)
|
||||||
|
return this;
|
||||||
|
return GetCommonType(GetNull(generator));
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonTypeEnum Type { get; private set; }
|
||||||
|
public JsonType InternalType { get; private set; }
|
||||||
|
public string AssignedName { get; private set; }
|
||||||
|
public string OriginalName { get; private set; }
|
||||||
|
public string NewAssignedName { get; private set; }
|
||||||
|
|
||||||
|
public void AssignName(string name) => AssignedName = name;
|
||||||
|
public void AssignOriginalName(string name) => OriginalName = name;
|
||||||
|
public void AssignNewAssignedName(string name) => NewAssignedName = name;
|
||||||
|
|
||||||
|
public bool MustCache
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Type switch
|
||||||
|
{
|
||||||
|
JsonTypeEnum.Array => true,
|
||||||
|
JsonTypeEnum.Object => true,
|
||||||
|
JsonTypeEnum.Anything => true,
|
||||||
|
JsonTypeEnum.Dictionary => true,
|
||||||
|
JsonTypeEnum.NonConstrained => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetReaderName()
|
||||||
|
{
|
||||||
|
if (Type is JsonTypeEnum.Anything or JsonTypeEnum.NullableSomething or JsonTypeEnum.NonConstrained)
|
||||||
|
{
|
||||||
|
return "ReadObject";
|
||||||
|
}
|
||||||
|
if (Type == JsonTypeEnum.Object)
|
||||||
|
{
|
||||||
|
return string.Format("ReadStronglyTypedObject<{0}>", AssignedName);
|
||||||
|
}
|
||||||
|
else if (Type == JsonTypeEnum.Array)
|
||||||
|
{
|
||||||
|
return string.Format("ReadArray<{0}>", InternalType.GetTypeName());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return string.Format("Read{0}", Enum.GetName(typeof(JsonTypeEnum), Type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonType GetInnermostType()
|
||||||
|
{
|
||||||
|
if (Type != JsonTypeEnum.Array)
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
if (InternalType.Type != JsonTypeEnum.Array)
|
||||||
|
return InternalType;
|
||||||
|
return InternalType.GetInnermostType();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsCollectionType() => Type is JsonTypeEnum.Array or JsonTypeEnum.Dictionary;
|
||||||
|
|
||||||
|
public string GetTypeName() => _Generator.CodeWriter.GetTypeName(this, _Generator);
|
||||||
|
|
||||||
|
public string GetJTokenType()
|
||||||
|
{
|
||||||
|
return Type switch
|
||||||
|
{
|
||||||
|
JsonTypeEnum.Boolean or JsonTypeEnum.Integer or JsonTypeEnum.Long or JsonTypeEnum.Float or JsonTypeEnum.Date or JsonTypeEnum.NullableBoolean or JsonTypeEnum.NullableInteger or JsonTypeEnum.NullableLong or JsonTypeEnum.NullableFloat or JsonTypeEnum.NullableDate or JsonTypeEnum.String => "JValue",
|
||||||
|
JsonTypeEnum.Array => "JArray",
|
||||||
|
JsonTypeEnum.Dictionary => "JObject",
|
||||||
|
JsonTypeEnum.Object => "JObject",
|
||||||
|
_ => "JToken",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonType GetCommonType(JsonType type2)
|
||||||
|
{
|
||||||
|
|
||||||
|
JsonTypeEnum commonType = GetCommonTypeEnum(Type, type2);
|
||||||
|
|
||||||
|
if (commonType == JsonTypeEnum.Array)
|
||||||
|
{
|
||||||
|
if (type2.Type == JsonTypeEnum.NullableSomething)
|
||||||
|
return this;
|
||||||
|
if (Type == JsonTypeEnum.NullableSomething)
|
||||||
|
return type2;
|
||||||
|
|
||||||
|
JsonType commonInternalType;
|
||||||
|
if (InternalType == null && type2.InternalType != null) // Handling the case Test_4 where the first array is an empty object
|
||||||
|
commonInternalType = type2.InternalType;
|
||||||
|
else
|
||||||
|
commonInternalType = InternalType.GetCommonType(type2.InternalType).MaybeMakeNullable(_Generator);
|
||||||
|
|
||||||
|
if (commonInternalType != InternalType)
|
||||||
|
return new JsonType(_Generator, JsonTypeEnum.Array) { InternalType = commonInternalType };
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (commonType == JsonTypeEnum.Dictionary)
|
||||||
|
//{
|
||||||
|
// var commonInternalType = InternalType.GetCommonType(type2.InternalType);
|
||||||
|
// if (commonInternalType != InternalType) return new JsonType(JsonTypeEnum.Dictionary) { InternalType = commonInternalType };
|
||||||
|
//}
|
||||||
|
|
||||||
|
if (Type == commonType)
|
||||||
|
return this;
|
||||||
|
return new JsonType(_Generator, commonType).MaybeMakeNullable(_Generator);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsNull(JsonTypeEnum type) => type == JsonTypeEnum.NullableSomething;
|
||||||
|
|
||||||
|
private JsonTypeEnum GetCommonTypeEnum(JsonTypeEnum type1, JsonType type2json)
|
||||||
|
{
|
||||||
|
if (type2json == null)
|
||||||
|
return type1;
|
||||||
|
|
||||||
|
JsonTypeEnum type2 = type2json.Type;
|
||||||
|
|
||||||
|
if (type1 == JsonTypeEnum.NonConstrained)
|
||||||
|
return type2;
|
||||||
|
if (type2 == JsonTypeEnum.NonConstrained)
|
||||||
|
return type1;
|
||||||
|
|
||||||
|
switch (type1)
|
||||||
|
{
|
||||||
|
case JsonTypeEnum.Boolean:
|
||||||
|
if (IsNull(type2))
|
||||||
|
return JsonTypeEnum.NullableBoolean;
|
||||||
|
if (type2 == JsonTypeEnum.Boolean)
|
||||||
|
return type1;
|
||||||
|
break;
|
||||||
|
case JsonTypeEnum.NullableBoolean:
|
||||||
|
if (IsNull(type2))
|
||||||
|
return type1;
|
||||||
|
if (type2 == JsonTypeEnum.Boolean)
|
||||||
|
return type1;
|
||||||
|
break;
|
||||||
|
case JsonTypeEnum.Integer:
|
||||||
|
if (IsNull(type2))
|
||||||
|
return JsonTypeEnum.NullableInteger;
|
||||||
|
if (type2 == JsonTypeEnum.Float)
|
||||||
|
return JsonTypeEnum.Float;
|
||||||
|
if (type2 == JsonTypeEnum.Long)
|
||||||
|
return JsonTypeEnum.Long;
|
||||||
|
if (type2 == JsonTypeEnum.Integer)
|
||||||
|
return type1;
|
||||||
|
break;
|
||||||
|
case JsonTypeEnum.NullableInteger:
|
||||||
|
if (IsNull(type2))
|
||||||
|
return type1;
|
||||||
|
if (type2 == JsonTypeEnum.Float)
|
||||||
|
return JsonTypeEnum.NullableFloat;
|
||||||
|
if (type2 == JsonTypeEnum.Long)
|
||||||
|
return JsonTypeEnum.NullableLong;
|
||||||
|
if (type2 == JsonTypeEnum.Integer)
|
||||||
|
return type1;
|
||||||
|
break;
|
||||||
|
case JsonTypeEnum.Float:
|
||||||
|
if (IsNull(type2))
|
||||||
|
return JsonTypeEnum.NullableFloat;
|
||||||
|
if (type2 == JsonTypeEnum.Float)
|
||||||
|
return type1;
|
||||||
|
if (type2 == JsonTypeEnum.Integer)
|
||||||
|
return type1;
|
||||||
|
if (type2 == JsonTypeEnum.Long)
|
||||||
|
return type1;
|
||||||
|
break;
|
||||||
|
case JsonTypeEnum.NullableFloat:
|
||||||
|
if (IsNull(type2))
|
||||||
|
return type1;
|
||||||
|
if (type2 == JsonTypeEnum.Float)
|
||||||
|
return type1;
|
||||||
|
if (type2 == JsonTypeEnum.Integer)
|
||||||
|
return type1;
|
||||||
|
if (type2 == JsonTypeEnum.Long)
|
||||||
|
return type1;
|
||||||
|
break;
|
||||||
|
case JsonTypeEnum.Long:
|
||||||
|
if (IsNull(type2))
|
||||||
|
return JsonTypeEnum.NullableLong;
|
||||||
|
if (type2 == JsonTypeEnum.Float)
|
||||||
|
return JsonTypeEnum.Float;
|
||||||
|
if (type2 == JsonTypeEnum.Integer)
|
||||||
|
return type1;
|
||||||
|
break;
|
||||||
|
case JsonTypeEnum.NullableLong:
|
||||||
|
if (IsNull(type2))
|
||||||
|
return type1;
|
||||||
|
if (type2 == JsonTypeEnum.Float)
|
||||||
|
return JsonTypeEnum.NullableFloat;
|
||||||
|
if (type2 == JsonTypeEnum.Integer)
|
||||||
|
return type1;
|
||||||
|
if (type2 == JsonTypeEnum.Long)
|
||||||
|
return type1;
|
||||||
|
break;
|
||||||
|
case JsonTypeEnum.Date:
|
||||||
|
if (IsNull(type2))
|
||||||
|
return JsonTypeEnum.NullableDate;
|
||||||
|
if (type2 == JsonTypeEnum.Date)
|
||||||
|
return JsonTypeEnum.Date;
|
||||||
|
break;
|
||||||
|
case JsonTypeEnum.NullableDate:
|
||||||
|
if (IsNull(type2))
|
||||||
|
return type1;
|
||||||
|
if (type2 == JsonTypeEnum.Date)
|
||||||
|
return type1;
|
||||||
|
break;
|
||||||
|
case JsonTypeEnum.NullableSomething:
|
||||||
|
if (IsNull(type2))
|
||||||
|
return type1;
|
||||||
|
if (type2 == JsonTypeEnum.String)
|
||||||
|
return JsonTypeEnum.String;
|
||||||
|
if (type2 == JsonTypeEnum.Integer)
|
||||||
|
return JsonTypeEnum.NullableInteger;
|
||||||
|
if (type2 == JsonTypeEnum.Float)
|
||||||
|
return JsonTypeEnum.NullableFloat;
|
||||||
|
if (type2 == JsonTypeEnum.Long)
|
||||||
|
return JsonTypeEnum.NullableLong;
|
||||||
|
if (type2 == JsonTypeEnum.Boolean)
|
||||||
|
return JsonTypeEnum.NullableBoolean;
|
||||||
|
if (type2 == JsonTypeEnum.Date)
|
||||||
|
return JsonTypeEnum.NullableDate;
|
||||||
|
if (type2 == JsonTypeEnum.Array)
|
||||||
|
return JsonTypeEnum.Array;
|
||||||
|
if (type2 == JsonTypeEnum.Object)
|
||||||
|
return JsonTypeEnum.Object;
|
||||||
|
break;
|
||||||
|
case JsonTypeEnum.Object:
|
||||||
|
if (IsNull(type2))
|
||||||
|
return type1;
|
||||||
|
if (type2 == JsonTypeEnum.Object)
|
||||||
|
return type1;
|
||||||
|
if (type2 == JsonTypeEnum.Dictionary)
|
||||||
|
throw new ArgumentException();
|
||||||
|
break;
|
||||||
|
case JsonTypeEnum.Dictionary:
|
||||||
|
throw new ArgumentException();
|
||||||
|
//if (IsNull(type2)) return type1;
|
||||||
|
//if (type2 == JsonTypeEnum.Object) return type1;
|
||||||
|
//if (type2 == JsonTypeEnum.Dictionary) return type1;
|
||||||
|
// break;
|
||||||
|
case JsonTypeEnum.Array:
|
||||||
|
if (IsNull(type2))
|
||||||
|
return type1;
|
||||||
|
if (type2 == JsonTypeEnum.Array)
|
||||||
|
return type1;
|
||||||
|
break;
|
||||||
|
case JsonTypeEnum.String:
|
||||||
|
if (IsNull(type2))
|
||||||
|
return type1;
|
||||||
|
if (type2 == JsonTypeEnum.String)
|
||||||
|
return type1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonTypeEnum.Anything;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsNull(JTokenType type) => type is JTokenType.Null or JTokenType.Undefined;
|
||||||
|
|
||||||
|
private static JsonTypeEnum GetFirstTypeEnum(JToken token)
|
||||||
|
{
|
||||||
|
JTokenType type = token.Type;
|
||||||
|
if (type == JTokenType.Integer)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if ((long)((JValue)token).Value < int.MaxValue)
|
||||||
|
return JsonTypeEnum.Integer;
|
||||||
|
else
|
||||||
|
return JsonTypeEnum.Long;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// Must be a BigInteger Number, either way make it as string
|
||||||
|
return JsonTypeEnum.String;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
JTokenType.Array => JsonTypeEnum.Array,
|
||||||
|
JTokenType.Boolean => JsonTypeEnum.Boolean,
|
||||||
|
JTokenType.Float => JsonTypeEnum.Float,
|
||||||
|
JTokenType.Null => JsonTypeEnum.NullableSomething,
|
||||||
|
JTokenType.Undefined => JsonTypeEnum.NullableSomething,
|
||||||
|
JTokenType.String => JsonTypeEnum.String,
|
||||||
|
JTokenType.Object => JsonTypeEnum.Object,
|
||||||
|
JTokenType.Date => JsonTypeEnum.Date,
|
||||||
|
_ => JsonTypeEnum.Anything,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<FieldInfo> Fields { get; internal set; }
|
||||||
|
public bool IsRoot { get; internal set; }
|
||||||
|
}
|
24
Json2CSharpCodeGenerator.Lib/JsonTypeEnum.cs
Normal file
24
Json2CSharpCodeGenerator.Lib/JsonTypeEnum.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// Copyright © 2010 Xamasoft
|
||||||
|
|
||||||
|
namespace Json2CSharpCodeGenerator.Lib;
|
||||||
|
|
||||||
|
public enum JsonTypeEnum
|
||||||
|
{
|
||||||
|
Anything,
|
||||||
|
String,
|
||||||
|
Boolean,
|
||||||
|
Integer,
|
||||||
|
Long,
|
||||||
|
Float,
|
||||||
|
Date,
|
||||||
|
NullableInteger,
|
||||||
|
NullableLong,
|
||||||
|
NullableFloat,
|
||||||
|
NullableBoolean,
|
||||||
|
NullableDate,
|
||||||
|
Object,
|
||||||
|
Array,
|
||||||
|
Dictionary,
|
||||||
|
NullableSomething,
|
||||||
|
NonConstrained
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<LangVersion>10.0</LangVersion>
|
||||||
|
<Nullable>disable</Nullable>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
|
<TargetFramework>net6.0-windows</TargetFramework>
|
||||||
|
<UseWindowsForms>true</UseWindowsForms>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Json2CSharpCodeGenerator.Lib\Json2CSharpCodeGenerator.Lib.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
386
Json2CSharpCodeGenerator.WinForms/MainForm.Designer.cs
generated
Normal file
386
Json2CSharpCodeGenerator.WinForms/MainForm.Designer.cs
generated
Normal file
@ -0,0 +1,386 @@
|
|||||||
|
|
||||||
|
namespace Json2CSharpCodeGenerator.Lib.WinForms
|
||||||
|
{
|
||||||
|
partial class MainForm
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose( bool disposing )
|
||||||
|
{
|
||||||
|
if( disposing && ( components != null ) )
|
||||||
|
{
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose( disposing );
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Windows Form Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
|
||||||
|
this.split = new System.Windows.Forms.SplitContainer();
|
||||||
|
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
||||||
|
this.jsonInputTextbox = new System.Windows.Forms.TextBox();
|
||||||
|
this.inputLabel = new System.Windows.Forms.Label();
|
||||||
|
this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
|
||||||
|
this.csharpOutputTextbox = new System.Windows.Forms.TextBox();
|
||||||
|
this.outputLabel = new System.Windows.Forms.Label();
|
||||||
|
this.toolStrip = new System.Windows.Forms.ToolStrip();
|
||||||
|
this.openButton = new System.Windows.Forms.ToolStripButton();
|
||||||
|
this.optsPascalCase = new System.Windows.Forms.ToolStripButton();
|
||||||
|
this.sep1 = new System.Windows.Forms.ToolStripSeparator();
|
||||||
|
this.optsAttributeMode = new System.Windows.Forms.ToolStripDropDownButton();
|
||||||
|
this.optAttribJP = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
this.optAttribJpn = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
this.sep2 = new System.Windows.Forms.ToolStripSeparator();
|
||||||
|
this.optMembersMode = new System.Windows.Forms.ToolStripDropDownButton();
|
||||||
|
this.optMemberProps = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
this.optMemberFields = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
this.sep3 = new System.Windows.Forms.ToolStripSeparator();
|
||||||
|
this.copyOutput = new System.Windows.Forms.ToolStripButton();
|
||||||
|
this.wrapText = new System.Windows.Forms.ToolStripButton();
|
||||||
|
this.optTypesMode = new System.Windows.Forms.ToolStripDropDownButton();
|
||||||
|
this.optTypesMutablePoco = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
this.optTypesImmutablePoco = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
this.optTypesRecords = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
this.statusStrip = new System.Windows.Forms.StatusStrip();
|
||||||
|
this.ofd = new System.Windows.Forms.OpenFileDialog();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.split)).BeginInit();
|
||||||
|
this.split.Panel1.SuspendLayout();
|
||||||
|
this.split.Panel2.SuspendLayout();
|
||||||
|
this.split.SuspendLayout();
|
||||||
|
this.tableLayoutPanel1.SuspendLayout();
|
||||||
|
this.tableLayoutPanel2.SuspendLayout();
|
||||||
|
this.toolStrip.SuspendLayout();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// split
|
||||||
|
//
|
||||||
|
this.split.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.split.Location = new System.Drawing.Point(0, 25);
|
||||||
|
this.split.Name = "split";
|
||||||
|
//
|
||||||
|
// split.Panel1
|
||||||
|
//
|
||||||
|
this.split.Panel1.Controls.Add(this.tableLayoutPanel1);
|
||||||
|
//
|
||||||
|
// split.Panel2
|
||||||
|
//
|
||||||
|
this.split.Panel2.Controls.Add(this.tableLayoutPanel2);
|
||||||
|
this.split.Size = new System.Drawing.Size(1288, 253);
|
||||||
|
this.split.SplitterDistance = 644;
|
||||||
|
this.split.TabIndex = 0;
|
||||||
|
//
|
||||||
|
// tableLayoutPanel1
|
||||||
|
//
|
||||||
|
this.tableLayoutPanel1.AllowDrop = true;
|
||||||
|
this.tableLayoutPanel1.ColumnCount = 1;
|
||||||
|
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||||
|
this.tableLayoutPanel1.Controls.Add(this.jsonInputTextbox, 0, 1);
|
||||||
|
this.tableLayoutPanel1.Controls.Add(this.inputLabel, 0, 0);
|
||||||
|
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
|
||||||
|
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||||
|
this.tableLayoutPanel1.RowCount = 2;
|
||||||
|
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||||
|
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||||
|
this.tableLayoutPanel1.Size = new System.Drawing.Size(644, 253);
|
||||||
|
this.tableLayoutPanel1.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// jsonInputTextbox
|
||||||
|
//
|
||||||
|
this.jsonInputTextbox.AcceptsReturn = true;
|
||||||
|
this.jsonInputTextbox.AcceptsTab = true;
|
||||||
|
this.jsonInputTextbox.AllowDrop = true;
|
||||||
|
this.jsonInputTextbox.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.jsonInputTextbox.Location = new System.Drawing.Point(3, 32);
|
||||||
|
this.jsonInputTextbox.Multiline = true;
|
||||||
|
this.jsonInputTextbox.Name = "jsonInputTextbox";
|
||||||
|
this.jsonInputTextbox.ScrollBars = System.Windows.Forms.ScrollBars.Both;
|
||||||
|
this.jsonInputTextbox.Size = new System.Drawing.Size(638, 218);
|
||||||
|
this.jsonInputTextbox.TabIndex = 0;
|
||||||
|
this.jsonInputTextbox.WordWrap = false;
|
||||||
|
//
|
||||||
|
// inputLabel
|
||||||
|
//
|
||||||
|
this.inputLabel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.inputLabel.Location = new System.Drawing.Point(1, 4);
|
||||||
|
this.inputLabel.Margin = new System.Windows.Forms.Padding(1, 4, 4, 2);
|
||||||
|
this.inputLabel.Name = "inputLabel";
|
||||||
|
this.inputLabel.Size = new System.Drawing.Size(639, 23);
|
||||||
|
this.inputLabel.TabIndex = 1;
|
||||||
|
this.inputLabel.Text = "Paste JSON Input or drag and drop a *.json file:";
|
||||||
|
//
|
||||||
|
// tableLayoutPanel2
|
||||||
|
//
|
||||||
|
this.tableLayoutPanel2.ColumnCount = 1;
|
||||||
|
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||||
|
this.tableLayoutPanel2.Controls.Add(this.csharpOutputTextbox, 0, 1);
|
||||||
|
this.tableLayoutPanel2.Controls.Add(this.outputLabel, 0, 0);
|
||||||
|
this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.tableLayoutPanel2.Location = new System.Drawing.Point(0, 0);
|
||||||
|
this.tableLayoutPanel2.Name = "tableLayoutPanel2";
|
||||||
|
this.tableLayoutPanel2.RowCount = 2;
|
||||||
|
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||||
|
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||||
|
this.tableLayoutPanel2.Size = new System.Drawing.Size(640, 253);
|
||||||
|
this.tableLayoutPanel2.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// csharpOutputTextbox
|
||||||
|
//
|
||||||
|
this.csharpOutputTextbox.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.csharpOutputTextbox.Location = new System.Drawing.Point(3, 32);
|
||||||
|
this.csharpOutputTextbox.Multiline = true;
|
||||||
|
this.csharpOutputTextbox.Name = "csharpOutputTextbox";
|
||||||
|
this.csharpOutputTextbox.ReadOnly = true;
|
||||||
|
this.csharpOutputTextbox.ScrollBars = System.Windows.Forms.ScrollBars.Both;
|
||||||
|
this.csharpOutputTextbox.Size = new System.Drawing.Size(634, 218);
|
||||||
|
this.csharpOutputTextbox.TabIndex = 0;
|
||||||
|
this.csharpOutputTextbox.WordWrap = false;
|
||||||
|
//
|
||||||
|
// outputLabel
|
||||||
|
//
|
||||||
|
this.outputLabel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.outputLabel.Location = new System.Drawing.Point(1, 4);
|
||||||
|
this.outputLabel.Margin = new System.Windows.Forms.Padding(1, 4, 4, 2);
|
||||||
|
this.outputLabel.Name = "outputLabel";
|
||||||
|
this.outputLabel.Size = new System.Drawing.Size(635, 23);
|
||||||
|
this.outputLabel.TabIndex = 1;
|
||||||
|
this.outputLabel.Text = "Generated C# output:";
|
||||||
|
//
|
||||||
|
// toolStrip
|
||||||
|
//
|
||||||
|
this.toolStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
|
this.openButton,
|
||||||
|
this.optsPascalCase,
|
||||||
|
this.sep1,
|
||||||
|
this.optsAttributeMode,
|
||||||
|
this.sep2,
|
||||||
|
this.optMembersMode,
|
||||||
|
this.sep3,
|
||||||
|
this.copyOutput,
|
||||||
|
this.wrapText,
|
||||||
|
this.optTypesMode});
|
||||||
|
this.toolStrip.Location = new System.Drawing.Point(0, 0);
|
||||||
|
this.toolStrip.Name = "toolStrip";
|
||||||
|
this.toolStrip.Size = new System.Drawing.Size(1288, 25);
|
||||||
|
this.toolStrip.TabIndex = 0;
|
||||||
|
//
|
||||||
|
// openButton
|
||||||
|
//
|
||||||
|
this.openButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
|
||||||
|
this.openButton.Name = "openButton";
|
||||||
|
this.openButton.Size = new System.Drawing.Size(49, 22);
|
||||||
|
this.openButton.Text = "Open...";
|
||||||
|
//
|
||||||
|
// optsPascalCase
|
||||||
|
//
|
||||||
|
this.optsPascalCase.Checked = true;
|
||||||
|
this.optsPascalCase.CheckOnClick = true;
|
||||||
|
this.optsPascalCase.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||||
|
this.optsPascalCase.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
|
||||||
|
this.optsPascalCase.Name = "optsPascalCase";
|
||||||
|
this.optsPascalCase.Size = new System.Drawing.Size(94, 22);
|
||||||
|
this.optsPascalCase.Text = "Use Pascal Case";
|
||||||
|
//
|
||||||
|
// sep1
|
||||||
|
//
|
||||||
|
this.sep1.Name = "sep1";
|
||||||
|
this.sep1.Size = new System.Drawing.Size(6, 25);
|
||||||
|
//
|
||||||
|
// optsAttributeMode
|
||||||
|
//
|
||||||
|
this.optsAttributeMode.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
|
||||||
|
this.optsAttributeMode.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
|
this.optAttribJP,
|
||||||
|
this.optAttribJpn});
|
||||||
|
this.optsAttributeMode.Name = "optsAttributeMode";
|
||||||
|
this.optsAttributeMode.Size = new System.Drawing.Size(106, 22);
|
||||||
|
this.optsAttributeMode.Text = "Attributes mode";
|
||||||
|
//
|
||||||
|
// optAttribJP
|
||||||
|
//
|
||||||
|
this.optAttribJP.CheckOnClick = true;
|
||||||
|
this.optAttribJP.Name = "optAttribJP";
|
||||||
|
this.optAttribJP.Size = new System.Drawing.Size(204, 22);
|
||||||
|
this.optAttribJP.Text = "Use [JsonProperty]";
|
||||||
|
//
|
||||||
|
// optAttribJpn
|
||||||
|
//
|
||||||
|
this.optAttribJpn.Checked = true;
|
||||||
|
this.optAttribJpn.CheckOnClick = true;
|
||||||
|
this.optAttribJpn.Name = "optAttribJpn";
|
||||||
|
this.optAttribJpn.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||||
|
this.optAttribJpn.Size = new System.Drawing.Size(204, 22);
|
||||||
|
this.optAttribJpn.Text = "Use [JsonPropertyName]";
|
||||||
|
//
|
||||||
|
// sep2
|
||||||
|
//
|
||||||
|
this.sep2.Name = "sep2";
|
||||||
|
this.sep2.Size = new System.Drawing.Size(6, 25);
|
||||||
|
//
|
||||||
|
// optMembersMode
|
||||||
|
//
|
||||||
|
this.optMembersMode.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
|
||||||
|
this.optMembersMode.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
|
this.optMemberProps,
|
||||||
|
this.optMemberFields});
|
||||||
|
this.optMembersMode.Name = "optMembersMode";
|
||||||
|
this.optMembersMode.Size = new System.Drawing.Size(99, 22);
|
||||||
|
this.optMembersMode.Text = "Member mode";
|
||||||
|
//
|
||||||
|
// optMemberProps
|
||||||
|
//
|
||||||
|
this.optMemberProps.Checked = true;
|
||||||
|
this.optMemberProps.CheckOnClick = true;
|
||||||
|
this.optMemberProps.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||||
|
this.optMemberProps.Name = "optMemberProps";
|
||||||
|
this.optMemberProps.Size = new System.Drawing.Size(180, 22);
|
||||||
|
this.optMemberProps.Text = "Use properties";
|
||||||
|
//
|
||||||
|
// optMemberFields
|
||||||
|
//
|
||||||
|
this.optMemberFields.CheckOnClick = true;
|
||||||
|
this.optMemberFields.Name = "optMemberFields";
|
||||||
|
this.optMemberFields.Size = new System.Drawing.Size(180, 22);
|
||||||
|
this.optMemberFields.Text = "Use fields";
|
||||||
|
//
|
||||||
|
// sep3
|
||||||
|
//
|
||||||
|
this.sep3.Name = "sep3";
|
||||||
|
this.sep3.Size = new System.Drawing.Size(6, 25);
|
||||||
|
//
|
||||||
|
// copyOutput
|
||||||
|
//
|
||||||
|
this.copyOutput.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right;
|
||||||
|
this.copyOutput.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
|
||||||
|
this.copyOutput.Name = "copyOutput";
|
||||||
|
this.copyOutput.Size = new System.Drawing.Size(167, 22);
|
||||||
|
this.copyOutput.Text = "Copy C# Output to Clipboard";
|
||||||
|
//
|
||||||
|
// wrapText
|
||||||
|
//
|
||||||
|
this.wrapText.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right;
|
||||||
|
this.wrapText.CheckOnClick = true;
|
||||||
|
this.wrapText.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
|
||||||
|
this.wrapText.Name = "wrapText";
|
||||||
|
this.wrapText.Size = new System.Drawing.Size(62, 22);
|
||||||
|
this.wrapText.Text = "Wrap text";
|
||||||
|
//
|
||||||
|
// optTypesMode
|
||||||
|
//
|
||||||
|
this.optTypesMode.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
|
||||||
|
this.optTypesMode.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
|
this.optTypesMutablePoco,
|
||||||
|
this.optTypesImmutablePoco,
|
||||||
|
this.optTypesRecords});
|
||||||
|
this.optTypesMode.Name = "optTypesMode";
|
||||||
|
this.optTypesMode.Size = new System.Drawing.Size(89, 22);
|
||||||
|
this.optTypesMode.Text = "Output types";
|
||||||
|
//
|
||||||
|
// optTypesMutablePoco
|
||||||
|
//
|
||||||
|
this.optTypesMutablePoco.CheckOnClick = true;
|
||||||
|
this.optTypesMutablePoco.Name = "optTypesMutablePoco";
|
||||||
|
this.optTypesMutablePoco.Size = new System.Drawing.Size(208, 22);
|
||||||
|
this.optTypesMutablePoco.Text = "Mutable POCO classes";
|
||||||
|
//
|
||||||
|
// optTypesImmutablePoco
|
||||||
|
//
|
||||||
|
this.optTypesImmutablePoco.Checked = true;
|
||||||
|
this.optTypesImmutablePoco.CheckOnClick = true;
|
||||||
|
this.optTypesImmutablePoco.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||||
|
this.optTypesImmutablePoco.Name = "optTypesImmutablePoco";
|
||||||
|
this.optTypesImmutablePoco.Size = new System.Drawing.Size(208, 22);
|
||||||
|
this.optTypesImmutablePoco.Text = "Immutable POCO classes";
|
||||||
|
//
|
||||||
|
// optTypesRecords
|
||||||
|
//
|
||||||
|
this.optTypesRecords.CheckOnClick = true;
|
||||||
|
this.optTypesRecords.Name = "optTypesRecords";
|
||||||
|
this.optTypesRecords.Size = new System.Drawing.Size(208, 22);
|
||||||
|
this.optTypesRecords.Text = "Immutable record classes";
|
||||||
|
//
|
||||||
|
// statusStrip
|
||||||
|
//
|
||||||
|
this.statusStrip.Location = new System.Drawing.Point(0, 278);
|
||||||
|
this.statusStrip.Name = "statusStrip";
|
||||||
|
this.statusStrip.Size = new System.Drawing.Size(1288, 22);
|
||||||
|
this.statusStrip.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// ofd
|
||||||
|
//
|
||||||
|
this.ofd.Filter = "JSON files (*.json)|*.json|All files (*.*)|*.*";
|
||||||
|
this.ofd.SupportMultiDottedExtensions = true;
|
||||||
|
//
|
||||||
|
// MainForm
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.ClientSize = new System.Drawing.Size(1288, 300);
|
||||||
|
this.Controls.Add(this.split);
|
||||||
|
this.Controls.Add(this.statusStrip);
|
||||||
|
this.Controls.Add(this.toolStrip);
|
||||||
|
this.DoubleBuffered = true;
|
||||||
|
this.Name = "MainForm";
|
||||||
|
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Show;
|
||||||
|
this.Text = "JSON-to-C#";
|
||||||
|
this.split.Panel1.ResumeLayout(false);
|
||||||
|
this.split.Panel2.ResumeLayout(false);
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.split)).EndInit();
|
||||||
|
this.split.ResumeLayout(false);
|
||||||
|
this.tableLayoutPanel1.ResumeLayout(false);
|
||||||
|
this.tableLayoutPanel1.PerformLayout();
|
||||||
|
this.tableLayoutPanel2.ResumeLayout(false);
|
||||||
|
this.tableLayoutPanel2.PerformLayout();
|
||||||
|
this.toolStrip.ResumeLayout(false);
|
||||||
|
this.toolStrip.PerformLayout();
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
this.PerformLayout();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.SplitContainer split;
|
||||||
|
private System.Windows.Forms.ToolStrip toolStrip;
|
||||||
|
private System.Windows.Forms.ToolStripButton optsPascalCase;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem optAttribJpn;
|
||||||
|
private System.Windows.Forms.ToolStripDropDownButton optMembersMode;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem optMemberProps;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem optMemberFields;
|
||||||
|
private System.Windows.Forms.TextBox jsonInputTextbox;
|
||||||
|
private System.Windows.Forms.TextBox csharpOutputTextbox;
|
||||||
|
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
|
||||||
|
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2;
|
||||||
|
private System.Windows.Forms.Label inputLabel;
|
||||||
|
private System.Windows.Forms.Label outputLabel;
|
||||||
|
private System.Windows.Forms.ToolStripSeparator sep1;
|
||||||
|
private System.Windows.Forms.ToolStripSeparator sep2;
|
||||||
|
private System.Windows.Forms.ToolStripSeparator sep3;
|
||||||
|
private System.Windows.Forms.ToolStripDropDownButton optsAttributeMode;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem optAttribJP;
|
||||||
|
private System.Windows.Forms.ToolStripButton copyOutput;
|
||||||
|
private System.Windows.Forms.StatusStrip statusStrip;
|
||||||
|
private System.Windows.Forms.ToolStripButton openButton;
|
||||||
|
private System.Windows.Forms.OpenFileDialog ofd;
|
||||||
|
private System.Windows.Forms.ToolStripButton wrapText;
|
||||||
|
private System.Windows.Forms.ToolStripDropDownButton optTypesMode;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem optTypesMutablePoco;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem optTypesImmutablePoco;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem optTypesRecords;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
496
Json2CSharpCodeGenerator.WinForms/MainForm.cs
Normal file
496
Json2CSharpCodeGenerator.WinForms/MainForm.cs
Normal file
@ -0,0 +1,496 @@
|
|||||||
|
using Microsoft.Win32;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace Json2CSharpCodeGenerator.Lib.WinForms;
|
||||||
|
|
||||||
|
public partial class MainForm : Form
|
||||||
|
{
|
||||||
|
private bool _PreventReentrancy = false;
|
||||||
|
|
||||||
|
public MainForm()
|
||||||
|
{
|
||||||
|
// `IconTitleFont` is what WinForms *should* be using by default.
|
||||||
|
// Need to set `Font` first, before `InitializeComponent();` to ensure font inheritance by controls in the form.
|
||||||
|
Font = SystemFonts.IconTitleFont;
|
||||||
|
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
ResetFonts();
|
||||||
|
|
||||||
|
// Also: https://docs.microsoft.com/en-us/dotnet/desktop/winforms/how-to-respond-to-font-scheme-changes-in-a-windows-forms-application?view=netframeworkdesktop-4.8
|
||||||
|
SystemEvents.UserPreferenceChanged += SystemEvents_UserPreferenceChanged;
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
openButton.Click += OpenButton_Click;
|
||||||
|
|
||||||
|
optAttribJP.CheckedChanged += OnAttributesModeCheckedChanged;
|
||||||
|
optAttribJpn.CheckedChanged += OnAttributesModeCheckedChanged;
|
||||||
|
//optAttribNone .CheckedChanged += OnAttributesModeCheckedChanged;
|
||||||
|
|
||||||
|
optMemberFields.CheckedChanged += OnMemberModeCheckedChanged;
|
||||||
|
optMemberProps.CheckedChanged += OnMemberModeCheckedChanged;
|
||||||
|
|
||||||
|
optTypesMutablePoco.CheckedChanged += OnOutputTypeModeCheckedChanged;
|
||||||
|
optTypesImmutablePoco.CheckedChanged += OnOutputTypeModeCheckedChanged;
|
||||||
|
optTypesRecords.CheckedChanged += OnOutputTypeModeCheckedChanged;
|
||||||
|
|
||||||
|
optsPascalCase.CheckedChanged += OnOptionsChanged;
|
||||||
|
|
||||||
|
wrapText.CheckedChanged += WrapText_CheckedChanged;
|
||||||
|
|
||||||
|
copyOutput.Click += CopyOutput_Click;
|
||||||
|
copyOutput.Enabled = false;
|
||||||
|
|
||||||
|
jsonInputTextbox.TextChanged += JsonInputTextbox_TextChanged;
|
||||||
|
jsonInputTextbox.DragDrop += JsonInputTextbox_DragDrop;
|
||||||
|
jsonInputTextbox.DragOver += JsonInputTextbox_DragOver;
|
||||||
|
//jsonInputTextbox.paste // annoyingly, it isn't (easily) feasible to hook/detect TextBox paste events, even globally... grrr.
|
||||||
|
|
||||||
|
// Invoke event-handlers to set initial toolstrip text:
|
||||||
|
optsAttributeMode.Tag = optsAttributeMode.Text + ": {0}";
|
||||||
|
optMembersMode.Tag = optMembersMode.Text + ": {0}";
|
||||||
|
optTypesMode.Tag = optTypesMode.Text + ": {0}";
|
||||||
|
|
||||||
|
OnAttributesModeCheckedChanged(optAttribJP, EventArgs.Empty);
|
||||||
|
OnMemberModeCheckedChanged(optMemberProps, EventArgs.Empty);
|
||||||
|
OnOutputTypeModeCheckedChanged(optTypesMutablePoco, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WrapText_CheckedChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
ToolStripButton tsb = (ToolStripButton)sender;
|
||||||
|
|
||||||
|
// For some reason, toggling WordWrap causes a text selection in `jsonInputTextbox`. So, doing this:
|
||||||
|
try
|
||||||
|
{
|
||||||
|
jsonInputTextbox.HideSelection = true;
|
||||||
|
// ayayayay: https://stackoverflow.com/questions/1140250/how-to-remove-the-focus-from-a-textbox-in-winforms
|
||||||
|
ActiveControl = toolStrip;
|
||||||
|
|
||||||
|
#if WINFORMS_TEXTBOX_GET_SCROLL_POSITION_WORKS_ARGH // It's non-trivial: https://stackoverflow.com/questions/4494162/change-scrollbar-position-in-textbox
|
||||||
|
//int idx1 = jsonInputTextbox.GetFirstCharIndexOfCurrentLine(); // but what is the "current line"?
|
||||||
|
int firstLineCharIndex = -1;
|
||||||
|
if( jsonInputTextbox.Height > 10 )
|
||||||
|
{
|
||||||
|
// https://stackoverflow.com/questions/10175400/maintain-textbox-scroll-position-while-adding-line
|
||||||
|
jsonInputTextbox.GetCharIndexFromPosition( new Point( 3, 3 ) );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
jsonInputTextbox.WordWrap = tsb.Checked;
|
||||||
|
csharpOutputTextbox.WordWrap = tsb.Checked;
|
||||||
|
|
||||||
|
#if WINFORMS_TEXTBOX_GET_SCROLL_POSITION_WORKS_ARGH
|
||||||
|
if( firstLineCharIndex > 0 ) // Greater than zero, not -1, because `GetCharIndexFromPosition` returns a meaningless zero sometimes.
|
||||||
|
{
|
||||||
|
jsonInputTextbox.SelectionStart = firstLineCharIndex;
|
||||||
|
jsonInputTextbox.ScrollToCaret();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
jsonInputTextbox.HideSelection = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region WinForms Taxes
|
||||||
|
|
||||||
|
private static Font GetMonospaceFont(float emFontSizePoints)
|
||||||
|
{
|
||||||
|
// See if Consolas or Lucida Sans Typewriter is available before falling-back:
|
||||||
|
string[] preferredFonts = new[] { "Consolas", "Lucida Sans Typewriter" };
|
||||||
|
foreach (string fontName in preferredFonts)
|
||||||
|
{
|
||||||
|
if (TestFont(fontName, emFontSizePoints))
|
||||||
|
{
|
||||||
|
return new Font(fontName, emFontSizePoints, FontStyle.Regular);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback:
|
||||||
|
return new Font(FontFamily.GenericMonospace, emSize: emFontSizePoints);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TestFont(string fontName, float emFontSizePoints)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using Font test = new(fontName, emFontSizePoints, FontStyle.Regular);
|
||||||
|
return test.Name == fontName;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e)
|
||||||
|
{
|
||||||
|
switch (e.Category)
|
||||||
|
{
|
||||||
|
case UserPreferenceCategory.Accessibility:
|
||||||
|
case UserPreferenceCategory.Window:
|
||||||
|
case UserPreferenceCategory.VisualStyle:
|
||||||
|
case UserPreferenceCategory.Menu:
|
||||||
|
ResetFonts();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetFonts()
|
||||||
|
{
|
||||||
|
Font = SystemFonts.IconTitleFont;
|
||||||
|
|
||||||
|
Font monospaceFont = GetMonospaceFont(emFontSizePoints: SystemFonts.IconTitleFont.SizeInPoints);
|
||||||
|
jsonInputTextbox.Font = monospaceFont;
|
||||||
|
csharpOutputTextbox.Font = monospaceFont;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnFormClosing(FormClosingEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnFormClosing(e);
|
||||||
|
|
||||||
|
if (!e.Cancel)
|
||||||
|
{
|
||||||
|
SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(SystemEvents_UserPreferenceChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods to ensure only a single checkbox-style menu item is checked at-a-time, and that the ToolStripDropDownButton's text indicates the currently selected option:
|
||||||
|
|
||||||
|
private void OnAttributesModeCheckedChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
EnsureSingleCheckedDropDownItemAndUpdateToolStripItemText((ToolStripMenuItem)sender, defaultItem: optAttribJP, parent: optsAttributeMode);
|
||||||
|
|
||||||
|
GenerateCSharp();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMemberModeCheckedChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
EnsureSingleCheckedDropDownItemAndUpdateToolStripItemText((ToolStripMenuItem)sender, defaultItem: optMemberProps, parent: optMembersMode);
|
||||||
|
|
||||||
|
GenerateCSharp();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnOutputTypeModeCheckedChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
EnsureSingleCheckedDropDownItemAndUpdateToolStripItemText((ToolStripMenuItem)sender, defaultItem: optTypesMutablePoco, parent: optTypesMode);
|
||||||
|
|
||||||
|
GenerateCSharp();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureSingleCheckedDropDownItemAndUpdateToolStripItemText(ToolStripMenuItem subject, ToolStripMenuItem defaultItem, ToolStripDropDownButton parent)
|
||||||
|
{
|
||||||
|
if (_PreventReentrancy)
|
||||||
|
return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_PreventReentrancy = true;
|
||||||
|
|
||||||
|
ToolStripMenuItem singleCheckedItem;
|
||||||
|
if (subject.Checked)
|
||||||
|
{
|
||||||
|
singleCheckedItem = subject;
|
||||||
|
UncheckOthers(subject, parent);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EnsureAtLeast1IsCheckedAfterItemWasUnchecked(subject, defaultItem, parent);
|
||||||
|
singleCheckedItem = parent.DropDownItems.Cast<ToolStripMenuItem>().Single(item => item.Checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
string parentTextFormat = (string)parent.Tag;
|
||||||
|
parent.Text = string.Format(format: parentTextFormat, arg0: singleCheckedItem.Text);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_PreventReentrancy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UncheckOthers(ToolStripMenuItem sender, ToolStripDropDownButton parent)
|
||||||
|
{
|
||||||
|
foreach (ToolStripMenuItem menuItem in parent.DropDownItems.Cast<ToolStripMenuItem>()) // I really hate old-style IEnumerable, *grumble*
|
||||||
|
{
|
||||||
|
if (!ReferenceEquals(menuItem, sender))
|
||||||
|
{
|
||||||
|
menuItem.Checked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EnsureAtLeast1IsCheckedAfterItemWasUnchecked(ToolStripMenuItem subject, ToolStripMenuItem defaultItem, ToolStripDropDownButton parent)
|
||||||
|
{
|
||||||
|
int countChecked = parent.DropDownItems.Cast<ToolStripMenuItem>().Count(item => item.Checked);
|
||||||
|
|
||||||
|
if (countChecked == 1)
|
||||||
|
{
|
||||||
|
// Is exactly 1 checked already? If so, then NOOP.
|
||||||
|
}
|
||||||
|
else if (countChecked > 1)
|
||||||
|
{
|
||||||
|
// If more than 1 are checked, then check only the default:
|
||||||
|
defaultItem.Checked = true;
|
||||||
|
UncheckOthers(sender: defaultItem, parent);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If none are checked, then *if* the unchecked item is NOT the default item, then check the default item:
|
||||||
|
if (!ReferenceEquals(subject, defaultItem))
|
||||||
|
{
|
||||||
|
defaultItem.Checked = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Otherwise, check the first non-default item:
|
||||||
|
ToolStripMenuItem nextBestItem = parent.DropDownItems.Cast<ToolStripMenuItem>().First(item => item != defaultItem);
|
||||||
|
nextBestItem.Checked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private void OnOptionsChanged(object sender, EventArgs e) => GenerateCSharp();
|
||||||
|
|
||||||
|
#region Drag and Drop
|
||||||
|
|
||||||
|
private void JsonInputTextbox_DragOver(object sender, DragEventArgs e)
|
||||||
|
{
|
||||||
|
bool acceptable =
|
||||||
|
e.Data.GetDataPresent(DataFormats.FileDrop) ||
|
||||||
|
// e.Data.GetDataPresent( DataFormats.Text ) ||
|
||||||
|
// e.Data.GetDataPresent( DataFormats.OemText ) ||
|
||||||
|
e.Data.GetDataPresent(DataFormats.UnicodeText, autoConvert: true)// ||
|
||||||
|
// e.Data.GetDataPresent( DataFormats.Html ) ||
|
||||||
|
// e.Data.GetDataPresent( DataFormats.StringFormat ) ||
|
||||||
|
// e.Data.GetDataPresent( DataFormats.Rtf )
|
||||||
|
;
|
||||||
|
|
||||||
|
if (acceptable)
|
||||||
|
{
|
||||||
|
e.Effect = DragDropEffects.Copy;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
e.Effect = DragDropEffects.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void JsonInputTextbox_DragDrop(object sender, DragEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Data.GetDataPresent(DataFormats.FileDrop))
|
||||||
|
{
|
||||||
|
string[] fileNames = (string[])e.Data.GetData(DataFormats.FileDrop);
|
||||||
|
if (fileNames.Length >= 1)
|
||||||
|
{
|
||||||
|
// hmm, maybe allow multiple files by concatenating them all into a horrible JSON array? :D
|
||||||
|
TryLoadJsonFile(fileNames[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (e.Data.GetDataPresent(DataFormats.UnicodeText, autoConvert: true))
|
||||||
|
{
|
||||||
|
statusStrip.Text = "";
|
||||||
|
|
||||||
|
string text = (string)e.Data.GetData(DataFormats.UnicodeText, autoConvert: true);
|
||||||
|
if (text != null)
|
||||||
|
{
|
||||||
|
jsonInputTextbox.Text = text; // This will invoke `GenerateCSharp()`.
|
||||||
|
|
||||||
|
statusStrip.Text = "Loaded JSON from drag and drop data.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>This regex won't match <c>\r\n</c>, only <c>\n</c>.</summary>
|
||||||
|
private static readonly Regex _OnlyUnixLineBreaks = new("(?<!\r)\n", RegexOptions.Compiled); // Don't use `[^\r]?\n` because it *will* match `\r\n`, and don't use `[^\r]\n` because it won't match a leading `$\n` in a file.
|
||||||
|
|
||||||
|
private static string RepairLineBreaks(string text)
|
||||||
|
{
|
||||||
|
if (_OnlyUnixLineBreaks.IsMatch(text))
|
||||||
|
{
|
||||||
|
return _OnlyUnixLineBreaks.Replace(text, replacement: "\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Open JSON file
|
||||||
|
|
||||||
|
private void OpenButton_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (ofd.ShowDialog(owner: this) == DialogResult.OK)
|
||||||
|
{
|
||||||
|
TryLoadJsonFile(ofd.FileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TryLoadJsonFile(string filePath)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(filePath))
|
||||||
|
{
|
||||||
|
csharpOutputTextbox.Text = "Error: an empty file path was specified.";
|
||||||
|
}
|
||||||
|
// else if ( filePath.IndexOfAny( Path.GetInvalidFileNameChars() ) > -1 )
|
||||||
|
// {
|
||||||
|
// const String fmt = "Invalid file path: \"{0}\"";
|
||||||
|
// csharpOutputTextbox.Text = String.Format( CultureInfo.CurrentCulture, fmt, filePath );
|
||||||
|
// }
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FileInfo jsonFileInfo;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
jsonFileInfo = new FileInfo(filePath);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
const string fmt = "Invalid file path: \"{0}\"\r\n{1}";
|
||||||
|
csharpOutputTextbox.Text = string.Format(CultureInfo.CurrentCulture, fmt, filePath, ex.ToString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TryLoadJsonFile(jsonFileInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TryLoadJsonFile(FileInfo jsonFile)
|
||||||
|
{
|
||||||
|
if (jsonFile is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
statusStrip.Text = "";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
jsonFile.Refresh();
|
||||||
|
if (jsonFile.Exists)
|
||||||
|
{
|
||||||
|
string jsonText = File.ReadAllText(jsonFile.FullName);
|
||||||
|
jsonInputTextbox.Text = jsonText; // This will invoke `GenerateCSharp()`.
|
||||||
|
|
||||||
|
statusStrip.Text = "Loaded \"" + jsonFile.FullName + "\" successfully.";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
csharpOutputTextbox.Text = string.Format(CultureInfo.CurrentCulture, "Error: File \"{0}\" does not exist.", jsonFile.FullName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
const string fmt = "Error loading file: \"{0}\"\r\n{1}";
|
||||||
|
|
||||||
|
csharpOutputTextbox.Text = string.Format(CultureInfo.CurrentCulture, fmt, jsonFile.FullName, ex.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private void ConfigureGenerator(IJsonClassGeneratorConfig config)
|
||||||
|
{
|
||||||
|
config.UsePascalCase = optsPascalCase.Checked;
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
if (optAttribJP.Checked)
|
||||||
|
{
|
||||||
|
config.AttributeLibrary = JsonLibrary.NewtonsoftJson;
|
||||||
|
}
|
||||||
|
else// implicit: ( optAttribJpn.Checked )
|
||||||
|
{
|
||||||
|
config.AttributeLibrary = JsonLibrary.SystemTextJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
if (optMemberProps.Checked)
|
||||||
|
{
|
||||||
|
config.MutableClasses.Members = OutputMembers.AsProperties;
|
||||||
|
}
|
||||||
|
else// implicit: ( optMemberFields.Checked )
|
||||||
|
{
|
||||||
|
config.MutableClasses.Members = OutputMembers.AsPublicFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
if (optTypesImmutablePoco.Checked)
|
||||||
|
{
|
||||||
|
config.OutputType = OutputTypes.ImmutableClass;
|
||||||
|
}
|
||||||
|
else if (optTypesMutablePoco.Checked)
|
||||||
|
{
|
||||||
|
config.OutputType = OutputTypes.MutableClass;
|
||||||
|
}
|
||||||
|
else// implicit: ( optTypesRecords.Checked )
|
||||||
|
{
|
||||||
|
config.OutputType = OutputTypes.ImmutableRecord;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void JsonInputTextbox_TextChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (_PreventReentrancy)
|
||||||
|
return;
|
||||||
|
_PreventReentrancy = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
jsonInputTextbox.Text = RepairLineBreaks(jsonInputTextbox.Text);
|
||||||
|
|
||||||
|
GenerateCSharp();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_PreventReentrancy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GenerateCSharp()
|
||||||
|
{
|
||||||
|
copyOutput.Enabled = false;
|
||||||
|
|
||||||
|
string jsonText = jsonInputTextbox.Text;
|
||||||
|
if (string.IsNullOrWhiteSpace(jsonText))
|
||||||
|
{
|
||||||
|
csharpOutputTextbox.Text = string.Empty;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonClassGenerator generator = new();
|
||||||
|
ConfigureGenerator(generator);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
StringBuilder sb = generator.GenerateClasses(jsonText, errorMessage: out string errorMessage);
|
||||||
|
if (!string.IsNullOrWhiteSpace(errorMessage))
|
||||||
|
{
|
||||||
|
csharpOutputTextbox.Text = "Error:\r\n" + errorMessage;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
csharpOutputTextbox.Text = sb.ToString();
|
||||||
|
copyOutput.Enabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
csharpOutputTextbox.Text = "Error:\r\n" + ex.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CopyOutput_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (csharpOutputTextbox.Text?.Length > 0)
|
||||||
|
{
|
||||||
|
Clipboard.SetText(csharpOutputTextbox.Text, TextDataFormat.UnicodeText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
Json2CSharpCodeGenerator.WinForms/Program.cs
Normal file
16
Json2CSharpCodeGenerator.WinForms/Program.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
namespace Json2CSharpCodeGenerator.Lib.WinForms;
|
||||||
|
|
||||||
|
static class Program
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The main entry point for the application.
|
||||||
|
/// </summary>
|
||||||
|
[STAThread]
|
||||||
|
static void Main()
|
||||||
|
{
|
||||||
|
// To customize application configuration such as set high DPI settings or default font,
|
||||||
|
// see https://aka.ms/applicationconfiguration.
|
||||||
|
ApplicationConfiguration.Initialize();
|
||||||
|
Application.Run(new MainForm());
|
||||||
|
}
|
||||||
|
}
|
28
Json2CSharpCodeGenerator.sln
Normal file
28
Json2CSharpCodeGenerator.sln
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 16
|
||||||
|
VisualStudioVersion = 16.0.30114.105
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Json2CSharpCodeGenerator.Lib", "Json2CSharpCodeGenerator.Lib\Json2CSharpCodeGenerator.Lib.csproj", "{8CC1EF05-B5FD-457A-98F4-603D81427955}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Json2CSharpCodeGenerator.WinForms", "Json2CSharpCodeGenerator.WinForms\Json2CSharpCodeGenerator.WinForms.csproj", "{0B91EC07-6FAA-4A51-BECD-8EA156B04976}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{8CC1EF05-B5FD-457A-98F4-603D81427955}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{8CC1EF05-B5FD-457A-98F4-603D81427955}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{8CC1EF05-B5FD-457A-98F4-603D81427955}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{8CC1EF05-B5FD-457A-98F4-603D81427955}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{0B91EC07-6FAA-4A51-BECD-8EA156B04976}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{0B91EC07-6FAA-4A51-BECD-8EA156B04976}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{0B91EC07-6FAA-4A51-BECD-8EA156B04976}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{0B91EC07-6FAA-4A51-BECD-8EA156B04976}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
64
License.txt
Normal file
64
License.txt
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
Microsoft Reciprocal License (Ms-RL)
|
||||||
|
|
||||||
|
This license governs use of the accompanying software. If you use the
|
||||||
|
software, you accept this license. If you do not accept the license, do not
|
||||||
|
use the software.
|
||||||
|
|
||||||
|
1. Definitions
|
||||||
|
The terms "reproduce," "reproduction," "derivative works," and "distribution"
|
||||||
|
have the same meaning here as under U.S. copyright law.
|
||||||
|
|
||||||
|
A "contribution" is the original software, or any additions or changes to the
|
||||||
|
software.
|
||||||
|
|
||||||
|
A "contributor" is any person that distributes its contribution under this
|
||||||
|
license.
|
||||||
|
|
||||||
|
"Licensed patents" are a contributor's patent claims that read directly on its
|
||||||
|
contribution.
|
||||||
|
|
||||||
|
2. Grant of Rights
|
||||||
|
(A) Copyright Grant- Subject to the terms of this license, including the
|
||||||
|
license conditions and limitations in section 3, each contributor grants
|
||||||
|
you a non-exclusive, worldwide, royalty-free copyright license to
|
||||||
|
reproduce its contribution, prepare derivative works of its contribution,
|
||||||
|
and distribute its contribution or any derivative works that you create.
|
||||||
|
|
||||||
|
(B) Patent Grant- Subject to the terms of this license, including the
|
||||||
|
license conditions and limitations in section 3, each contributor grants
|
||||||
|
you a non-exclusive, worldwide, royalty-free license under its licensed
|
||||||
|
patents to make, have made, use, sell, offer for sale, import, and/or
|
||||||
|
otherwise dispose of its contribution in the software or derivative works
|
||||||
|
of the contribution in the software.
|
||||||
|
|
||||||
|
3. Conditions and Limitations
|
||||||
|
(A) Reciprocal Grants- For any file you distribute that contains code
|
||||||
|
from the software (in source code or binary format), you must provide
|
||||||
|
recipients the source code to that file along with a copy of this
|
||||||
|
license, which license will govern that file. You may license other files
|
||||||
|
that are entirely your own work and do not contain code from the software
|
||||||
|
under any terms you choose.
|
||||||
|
|
||||||
|
(B) No Trademark License- This license does not grant you rights to use
|
||||||
|
any contributors' name, logo, or trademarks.
|
||||||
|
|
||||||
|
(C) If you bring a patent claim against any contributor over patents that
|
||||||
|
you claim are infringed by the software, your patent license from such
|
||||||
|
contributor to the software ends automatically.
|
||||||
|
|
||||||
|
(D) If you distribute any portion of the software, you must retain all
|
||||||
|
copyright, patent, trademark, and attribution notices that are present in
|
||||||
|
the software.
|
||||||
|
|
||||||
|
(E) If you distribute any portion of the software in source code form,
|
||||||
|
you may do so only under this license by including a complete copy of
|
||||||
|
this license with your distribution. If you distribute any portion of the
|
||||||
|
software in compiled or object code form, you may only do so under a
|
||||||
|
license that complies with this license.
|
||||||
|
|
||||||
|
(F) The software is licensed "as-is." You bear the risk of using it. The
|
||||||
|
contributors give no express warranties, guarantees, or conditions. You
|
||||||
|
may have additional consumer rights under your local laws which this
|
||||||
|
license cannot change. To the extent permitted under your local laws, the
|
||||||
|
contributors exclude the implied warranties of merchantability, fitness
|
||||||
|
for a particular purpose and non-infringement.
|
34
README.md
Normal file
34
README.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
## Special Thanks to Our Contributors !!
|
||||||
|
Updated Pluralization Package & Test cleanup
|
||||||
|
[marblekirby](https://github.com/marblekirby) <br/>
|
||||||
|
|
||||||
|
Implemented Immutable Classes Feature <br/>
|
||||||
|
[Holly-HaCKer](https://github.com/HoLLy-HaCKeR) <br/>
|
||||||
|
|
||||||
|
Implemented Immutable Classes Feature <br/>
|
||||||
|
[Jehoel](https://github.com/Jehoel) <br/>
|
||||||
|
|
||||||
|
Assisted in Bug Fixing <br/>
|
||||||
|
[tyeth](https://github.com/tyeth), [dogac00](https://github.com/dogac00)
|
||||||
|
|
||||||
|
## Contribution Guidelines
|
||||||
|
1 - New ideas, suggestions and improvements are welcomed. <br />
|
||||||
|
2 - Create a unit test with the expected output and work from there <br />
|
||||||
|
3 - If you're submitting a new bug, check to see if It's already submitted. Propose a brief solution to the problem to make it easier for other people to fix. <br />
|
||||||
|
4 - Enjoy coding :) ! <br />
|
||||||
|
|
||||||
|
## Bug Fixing
|
||||||
|
### 1- Choose a problem from the issues labeled "Bug" or "Help Wanted"
|
||||||
|
### 2- Clone the repository and build it
|
||||||
|
### 3- Create a Unit Test using "CreatTest.ps1" Powershell Script
|
||||||
|
**>>>>> Unit Test Name should be in the format of "[TestNumber]_[DescribeProblem]"**
|
||||||
|
* Run the script in Powershell
|
||||||
|
* This will create 3 files: the csharp test, the Json Input text file, and the Output C# or JAVA etc.. file
|
||||||
|

|
||||||
|
* Start Debugging, Put some test Json and Get to Know the solution
|
||||||
|
*
|
||||||
|
## Implementing New Code Generators : F#, Typescript etc.. ?
|
||||||
|
Implement the below functions
|
||||||
|

|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user