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