Converted to net6.0

This commit is contained in:
Mike Phares 2022-08-13 13:01:47 -07:00
commit 2385affccb
32 changed files with 4847 additions and 0 deletions

244
.editorconfig Normal file
View 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
View 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
View File

@ -0,0 +1 @@
[]

26
.vscode/launch.json vendored Normal file
View 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
View File

@ -0,0 +1,6 @@
{
"cSpell.enabled": false,
"files.exclude": {
"**/.git": false
}
}

41
.vscode/tasks.json vendored Normal file
View 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"
}
]
}

View 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();
}
}

View 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."),
};
}
}

View 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); */");
}
}
}

View 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."),
};
}
}

View File

@ -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();
}
}
}

View File

@ -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");
}

View 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();
}
}
}

View 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);
}
}
}
}
}

View 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();
}
}
}
}

View 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");
}
}
}

View 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)));
}

View 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);
}

View 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);
}

View 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 &quot;secondary&quot; 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&lt;T&gt;</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&lt;T&gt;</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&lt;String&gt; 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&lt;String&gt; 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>&quot;<c>[JsonPropertyName(&quot;<paramref name="field"/>.<see cref="FieldInfo.JsonMemberName"/>&quot;)]</c>&quot; (for <c>System.Text.Json</c>).</item>
/// <item>&quot;<c>[JsonProperty(&quot;<paramref name="field"/>.<see cref="FieldInfo.JsonMemberName"/>&quot;)]</c>&quot; (for <c>Newtonsoft.Json</c>).</item>
/// <item>&quot;<c>[property: JsonPropertyName(&quot;<paramref name="field"/>.<see cref="FieldInfo.JsonMemberName"/>&quot;)]</c>&quot; (for <c>System.Text.Json</c>) when <see cref="IJsonClassGeneratorConfig.RecordTypes"/> is <see langword="true"/>.</item>
/// <item>&quot;<c>[property: JsonProperty(&quot;<paramref name="field"/>.<see cref="FieldInfo.JsonMemberName"/>&quot;)]</c>&quot; (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);
}

View File

@ -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>

View 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();
}
}

View 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;
}
}

View 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; }
}

View 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
}

View File

@ -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>

View 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;
}
}

View 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);
}
}
}

View 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());
}
}

View 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
View 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
View 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
![alt text](https://json2csharp.azureedge.net/images/github-repo-images/Test%20Files.png)
* Start Debugging, Put some test Json and Get to Know the solution
*
## Implementing New Code Generators : F#, Typescript etc.. ?
Implement the below functions
![alt text](https://json2csharp.azureedge.net/images/github-repo-images/IMPLEMENT.png)