diff --git a/src/Riverside.JsonBinder/Serialization/CSharpSerializer.cs b/src/Riverside.JsonBinder/Serialization/CSharpSerializer.cs index 25856fe..453c08d 100644 --- a/src/Riverside.JsonBinder/Serialization/CSharpSerializer.cs +++ b/src/Riverside.JsonBinder/Serialization/CSharpSerializer.cs @@ -33,8 +33,8 @@ private void ProcessNode(JsonNode node, string className, List classes) var classDef = $"public class {className}\n{{"; foreach (var property in obj) { - var propType = GetType(property.Value, property.Key); - classDef += $"\n public {propType} {property.Key} {{ get; set; }}"; + var propType = GetType(property.Value, ToPascalCase(property.Key)); + classDef += $"\n public {propType} {ToPascalCase(property.Key)} {{ get; set; }}"; } classDef += "\n}"; classes.Add(classDef); @@ -43,24 +43,17 @@ private void ProcessNode(JsonNode node, string className, List classes) { if (property.Value is JsonObject || property.Value is JsonArray) { - ProcessNode(property.Value, property.Key, classes); + ProcessNode(property.Value, ToPascalCase(property.Key), classes); } } } - else if (node is JsonArray array) + else if (node is JsonArray array && array.Count > 0) { - string elementType = "object"; - if (array.Count > 0) + var firstElement = array[0]; + if (firstElement is JsonObject || firstElement is JsonArray) { - var firstElement = array[0]; - elementType = GetType(firstElement, className); - if (firstElement is JsonObject || firstElement is JsonArray) - { - ProcessNode(firstElement, className + "Item", classes); - elementType = className + "Item"; - } + ProcessNode(firstElement, className + "Item", classes); } - classes.Add($"public class {className}\n{{\n public List<{elementType}> Items {{ get; set; }} = new List<{elementType}>();\n}}"); } } @@ -75,7 +68,7 @@ public override string GetType(JsonNode? node, string propertyName) return node switch { JsonObject => propertyName, - JsonArray => $"List<{propertyName}>", + JsonArray => $"List<{propertyName}Item>", JsonValue value when value.TryGetValue(out _) => "int", JsonValue value when value.TryGetValue(out _) => "double", JsonValue value when value.TryGetValue(out _) => "string", diff --git a/src/Riverside.JsonBinder/Serialization/JavaScriptSerializer.cs b/src/Riverside.JsonBinder/Serialization/JavaScriptSerializer.cs index 1968ea0..763ad8a 100644 --- a/src/Riverside.JsonBinder/Serialization/JavaScriptSerializer.cs +++ b/src/Riverside.JsonBinder/Serialization/JavaScriptSerializer.cs @@ -26,40 +26,50 @@ public override string GenerateClasses(JsonNode node, string className) /// The JSON node to process. /// The name of the class to generate. /// The list of generated class definitions. + private void ProcessNode(JsonNode node, string className, List classes) { if (node is JsonObject obj) { - var classDef = $"class {className} {{\n constructor() {{"; + var classDef = $"class {className} {{"; + var constructorAssignments = new List(); + + foreach (var property in obj) + { + constructorAssignments.Add($" this.{property.Key} = null;"); + } + + classDef += "\n constructor() {"; + classDef += "\n" + string.Join("\n", constructorAssignments); + classDef += "\n }"; + + // Add JSDoc comments for better IDE support + classDef += "\n\n /**"; foreach (var property in obj) { - classDef += $"\n this.{property.Key} = null;"; + var propType = GetType(property.Value, className); + classDef += $"\n * @type {{{propType}}}"; } - classDef += "\n }\n}"; + classDef += "\n */"; + + classDef += "\n}"; classes.Add(classDef); foreach (var property in obj) { if (property.Value is JsonObject || property.Value is JsonArray) { - ProcessNode(property.Value, property.Key, classes); + ProcessNode(property.Value, ToPascalCase(property.Key), classes); } } } - else if (node is JsonArray array) + else if (node is JsonArray array && array.Count > 0) { - string elementType = "any"; - if (array.Count > 0) + var firstElement = array[0]; + if (firstElement is JsonObject || firstElement is JsonArray) { - var firstElement = array[0]; - elementType = GetType(firstElement, className); - if (firstElement is JsonObject || firstElement is JsonArray) - { - ProcessNode(firstElement, className + "Item", classes); - elementType = className + "Item"; - } + ProcessNode(firstElement, className + "Item", classes); } - classes.Add($"class {className} {{\n constructor() {{\n this.items = [];\n }}\n}}"); } } @@ -73,13 +83,13 @@ public override string GetType(JsonNode? node, string propertyName) { return node switch { - JsonObject => "object", - JsonArray => "array", + JsonObject => propertyName, + JsonArray => $"Array<{propertyName}Item>", JsonValue value when value.TryGetValue(out _) => "number", JsonValue value when value.TryGetValue(out _) => "number", JsonValue value when value.TryGetValue(out _) => "string", JsonValue value when value.TryGetValue(out _) => "boolean", - _ => "any" + _ => "*" }; } } diff --git a/src/Riverside.JsonBinder/Serialization/JavaSerializer.cs b/src/Riverside.JsonBinder/Serialization/JavaSerializer.cs index 6aea66a..e38a08c 100644 --- a/src/Riverside.JsonBinder/Serialization/JavaSerializer.cs +++ b/src/Riverside.JsonBinder/Serialization/JavaSerializer.cs @@ -31,38 +31,37 @@ private void ProcessNode(JsonNode node, string className, List classes) if (node is JsonObject obj) { var classDef = $"public class {className} {{"; + var fields = new List(); + var methods = new List(); + foreach (var property in obj) { - var propType = GetType(property.Value, property.Key); - classDef += $"\n private {propType} {property.Key};"; - classDef += $"\n public {propType} get{property.Key}() {{ return {property.Key}; }}"; - classDef += $"\n public void set{property.Key}({propType} {property.Key}) {{ this.{property.Key} = {property.Key}; }}"; + var propName = ToPascalCase(property.Key); + var propType = GetType(property.Value, propName); + + fields.Add($" private {propType} {property.Key};"); + methods.Add($" public {propType} get{propName}() {{ return {property.Key}; }}"); + methods.Add($" public void set{propName}({propType} {property.Key}) {{ this.{property.Key} = {property.Key}; }}"); } - classDef += "\n}"; + + classDef += "\n" + string.Join("\n", fields) + "\n\n" + string.Join("\n", methods) + "\n}"; classes.Add(classDef); foreach (var property in obj) { if (property.Value is JsonObject || property.Value is JsonArray) { - ProcessNode(property.Value, property.Key, classes); + ProcessNode(property.Value, ToPascalCase(property.Key), classes); } } } - else if (node is JsonArray array) + else if (node is JsonArray array && array.Count > 0) { - string elementType = "Object"; - if (array.Count > 0) + var firstElement = array[0]; + if (firstElement is JsonObject || firstElement is JsonArray) { - var firstElement = array[0]; - elementType = GetType(firstElement, className); - if (firstElement is JsonObject || firstElement is JsonArray) - { - ProcessNode(firstElement, className + "Item", classes); - elementType = className + "Item"; - } + ProcessNode(firstElement, className + "Item", classes); } - classes.Add($"public class {className} {{\n private List<{elementType}> items;\n public List<{elementType}> getItems() {{ return items; }}\n public void setItems(List<{elementType}> items) {{ this.items = items; }}\n}}"); } } @@ -77,7 +76,7 @@ public override string GetType(JsonNode? node, string propertyName) return node switch { JsonObject => propertyName, - JsonArray => $"List<{propertyName}>", + JsonArray => $"List<{propertyName}Item>", JsonValue value when value.TryGetValue(out _) => "int", JsonValue value when value.TryGetValue(out _) => "double", JsonValue value when value.TryGetValue(out _) => "String", diff --git a/src/Riverside.JsonBinder/Serialization/LanguageSerializer.cs b/src/Riverside.JsonBinder/Serialization/LanguageSerializer.cs index 43307d7..6cbb401 100644 --- a/src/Riverside.JsonBinder/Serialization/LanguageSerializer.cs +++ b/src/Riverside.JsonBinder/Serialization/LanguageSerializer.cs @@ -22,4 +22,17 @@ public abstract class LanguageSerializer /// The name of the property. /// The type as a string. public abstract string GetType(JsonNode? node, string propertyName); + public string ToPascalCase(string input) + { + if (string.IsNullOrEmpty(input)) + { + return input; + } + + // Split the input into words using non-alphanumeric characters as delimiters. + var words = input.Split(new[] { '_', '-', ' ', '.' }, StringSplitOptions.RemoveEmptyEntries); + + // Capitalize the first letter of each word and join them. + return string.Concat(words.Select(word => char.ToUpper(word[0]) + word.Substring(1).ToLower())); + } } diff --git a/src/Riverside.JsonBinder/Serialization/PHPSerializer.cs b/src/Riverside.JsonBinder/Serialization/PHPSerializer.cs index 024489d..252b15a 100644 --- a/src/Riverside.JsonBinder/Serialization/PHPSerializer.cs +++ b/src/Riverside.JsonBinder/Serialization/PHPSerializer.cs @@ -30,36 +30,42 @@ private void ProcessNode(JsonNode node, string className, List classes) { if (node is JsonObject obj) { - var classDef = $"class {className} {{\n"; + var classDef = $"class {className}\n{{"; + var properties = new List(); + var methods = new List(); + foreach (var property in obj) { - classDef += $"\n public ${property.Key};"; + var propName = property.Key; + var propType = GetType(property.Value, className); + properties.Add($" private {propType} ${propName};"); + + var pascalPropName = ToPascalCase(propName); + methods.Add($" public function get{pascalPropName}() {{ return $this->{propName}; }}"); + methods.Add($" public function set{pascalPropName}({propType} ${propName}) {{ $this->{propName} = ${propName}; }}"); } + + classDef += "\n" + string.Join("\n", properties); + classDef += "\n\n" + string.Join("\n", methods); classDef += "\n}"; + classes.Add(classDef); foreach (var property in obj) { if (property.Value is JsonObject || property.Value is JsonArray) { - ProcessNode(property.Value, property.Key, classes); + ProcessNode(property.Value, ToPascalCase(property.Key), classes); } } } - else if (node is JsonArray array) + else if (node is JsonArray array && array.Count > 0) { - string elementType = "mixed"; - if (array.Count > 0) + var firstElement = array[0]; + if (firstElement is JsonObject || firstElement is JsonArray) { - var firstElement = array[0]; - elementType = GetType(firstElement, className); - if (firstElement is JsonObject || firstElement is JsonArray) - { - ProcessNode(firstElement, className + "Item", classes); - elementType = className + "Item"; - } + ProcessNode(firstElement, className + "Item", classes); } - classes.Add($"class {className} {{\n public array ${className} = [];\n}}"); } } @@ -73,13 +79,13 @@ public override string GetType(JsonNode? node, string propertyName) { return node switch { - JsonObject => "object", - JsonArray => "array", + JsonObject => propertyName, + JsonArray => "array", // PHP 7+ type hint JsonValue value when value.TryGetValue(out _) => "int", JsonValue value when value.TryGetValue(out _) => "float", JsonValue value when value.TryGetValue(out _) => "string", JsonValue value when value.TryGetValue(out _) => "bool", - _ => "mixed" + _ => "mixed" // PHP 8+ type hint }; } } diff --git a/src/Riverside.JsonBinder/Serialization/PythonSerializer.cs b/src/Riverside.JsonBinder/Serialization/PythonSerializer.cs index c7f0984..a240b5a 100644 --- a/src/Riverside.JsonBinder/Serialization/PythonSerializer.cs +++ b/src/Riverside.JsonBinder/Serialization/PythonSerializer.cs @@ -1,4 +1,5 @@ -using System.Text.Json.Nodes; +using System.Linq; +using System.Text.Json.Nodes; namespace Riverside.JsonBinder.Serialization; @@ -26,43 +27,45 @@ public override string GenerateClasses(JsonNode node, string className) /// The JSON node to process. /// The name of the class to generate. /// The list of generated class definitions. + /// + private void ProcessNode(JsonNode node, string className, List classes) { if (node is JsonObject obj) { - var classDef = $"class {className}:\n def __init__(self):"; + var classDef = $"class {className}:"; + var initDef = " def __init__(self):"; + var annotations = new List(); + foreach (var property in obj) { - var propType = GetType(property.Value, property.Key); - classDef += $"\n self.{property.Key}: {propType} = None"; + var propName = property.Key.ToLower(); + var propType = GetType(property.Value, className); + annotations.Add($" {propName}: Optional[{propType}]"); + initDef += $"\n self.{propName}: Optional[{propType}] = None"; } - classes.Add(classDef); + + classes.Add($"{classDef}\n{string.Join("\n", annotations)}\n\n{initDef}"); foreach (var property in obj) { if (property.Value is JsonObject || property.Value is JsonArray) { - ProcessNode(property.Value, property.Key, classes); + ProcessNode(property.Value, ToPascalCase(property.Key), classes); } } } - else if (node is JsonArray array) + else if (node is JsonArray array && array.Count > 0) { - string elementType = "Any"; - if (array.Count > 0) + var firstElement = array[0]; + if (firstElement is JsonObject || firstElement is JsonArray) { - var firstElement = array[0]; - elementType = GetType(firstElement, className); - if (firstElement is JsonObject || firstElement is JsonArray) - { - ProcessNode(firstElement, className + "Item", classes); - elementType = className + "Item"; - } + ProcessNode(firstElement, className + "Item", classes); } - classes.Add($"class {className}:\n def __init__(self):\n self.items: List[{elementType}] = []"); } } + /// /// Gets the Python type for a given JSON node. /// @@ -73,13 +76,13 @@ public override string GetType(JsonNode? node, string propertyName) { return node switch { - JsonObject => "dict", - JsonArray => "list", + JsonObject => propertyName, + JsonArray => $"List[{propertyName}Item]", JsonValue value when value.TryGetValue(out _) => "int", JsonValue value when value.TryGetValue(out _) => "float", JsonValue value when value.TryGetValue(out _) => "str", JsonValue value when value.TryGetValue(out _) => "bool", - _ => "Any" + _ => "object" }; } } diff --git a/src/Riverside.JsonBinder/Serialization/RubySerializer.cs b/src/Riverside.JsonBinder/Serialization/RubySerializer.cs index 4b8bcb9..b608190 100644 --- a/src/Riverside.JsonBinder/Serialization/RubySerializer.cs +++ b/src/Riverside.JsonBinder/Serialization/RubySerializer.cs @@ -30,35 +30,50 @@ private void ProcessNode(JsonNode node, string className, List classes) { if (node is JsonObject obj) { - var classDef = $"class {className}\n attr_accessor "; - classDef += string.Join(", ", obj.Select(property => $":{property.Key}")); + var classDef = $"class {className}"; + var properties = new List(); + var typeSignatures = new List(); + + // Add type signatures using Ruby 3 syntax + foreach (var property in obj) + { + var propType = GetType(property.Value, ToPascalCase(property.Key)); + typeSignatures.Add($" sig {{ returns({propType}).nilable }}"); + properties.Add($" attr_accessor :{property.Key}"); + } + + // Add initialize method + var initMethod = " def initialize\n"; + foreach (var property in obj) + { + initMethod += $" @{property.Key} = nil\n"; + } + initMethod += " end"; + + classDef += "\n extend T::Sig\n\n"; + classDef += string.Join("\n", typeSignatures) + "\n\n"; + classDef += string.Join("\n", properties) + "\n\n"; + classDef += initMethod + "\nend"; + classes.Add(classDef); foreach (var property in obj) { if (property.Value is JsonObject || property.Value is JsonArray) { - ProcessNode(property.Value, property.Key, classes); + ProcessNode(property.Value, ToPascalCase(property.Key), classes); } } } - else if (node is JsonArray array) + else if (node is JsonArray array && array.Count > 0) { - string elementType = "Object"; - if (array.Count > 0) + var firstElement = array[0]; + if (firstElement is JsonObject || firstElement is JsonArray) { - var firstElement = array[0]; - elementType = GetType(firstElement, className); - if (firstElement is JsonObject || firstElement is JsonArray) - { - ProcessNode(firstElement, className + "Item", classes); - elementType = className + "Item"; - } + ProcessNode(firstElement, className + "Item", classes); } - classes.Add($"class {className}\n attr_accessor :items\n\n def initialize\n @items = []\n end\nend"); } } - /// /// Gets the Ruby type for a given JSON node. /// @@ -69,13 +84,13 @@ public override string GetType(JsonNode? node, string propertyName) { return node switch { - JsonObject => "Hash", - JsonArray => "Array", + JsonObject => propertyName, + JsonArray => $"T::Array[{propertyName}Item]", JsonValue value when value.TryGetValue(out _) => "Integer", JsonValue value when value.TryGetValue(out _) => "Float", JsonValue value when value.TryGetValue(out _) => "String", - JsonValue value when value.TryGetValue(out _) => "Boolean", - _ => "Object" + JsonValue value when value.TryGetValue(out _) => "T::Boolean", + _ => "T.untyped" }; } } diff --git a/src/Riverside.JsonBinder/Serialization/SwiftSerializer.cs b/src/Riverside.JsonBinder/Serialization/SwiftSerializer.cs index 08f73b8..4bf04b3 100644 --- a/src/Riverside.JsonBinder/Serialization/SwiftSerializer.cs +++ b/src/Riverside.JsonBinder/Serialization/SwiftSerializer.cs @@ -26,41 +26,40 @@ public override string GenerateClasses(JsonNode node, string className) /// The JSON node to process. /// The name of the struct to generate. /// The list of generated struct definitions. + private void ProcessNode(JsonNode node, string className, List classes) { if (node is JsonObject obj) { - var classDef = $"struct {className} {{"; + var classDef = $"struct {className}: Codable {{"; + var properties = new List(); + foreach (var property in obj) { - var propType = GetType(property.Value, property.Key); - classDef += $"\n var {property.Key}: {propType}?"; + var propType = GetType(property.Value, ToPascalCase(property.Key)); + properties.Add($" var {property.Key}: {propType}?"); } + + classDef += "\n" + string.Join("\n", properties); classDef += "\n}"; + classes.Add(classDef); foreach (var property in obj) { if (property.Value is JsonObject || property.Value is JsonArray) { - ProcessNode(property.Value, property.Key, classes); + ProcessNode(property.Value, ToPascalCase(property.Key), classes); } } } - else if (node is JsonArray array) + else if (node is JsonArray array && array.Count > 0) { - string elementType = "Any"; - if (array.Count > 0) + var firstElement = array[0]; + if (firstElement is JsonObject || firstElement is JsonArray) { - var firstElement = array[0]; - elementType = GetType(firstElement, className); - if (firstElement is JsonObject || firstElement is JsonArray) - { - ProcessNode(firstElement, className + "Item", classes); - elementType = className + "Item"; - } + ProcessNode(firstElement, className + "Item", classes); } - classes.Add($"struct {className} {{\n var items: [{elementType}] = []\n}}"); } } @@ -70,12 +69,13 @@ private void ProcessNode(JsonNode node, string className, List classes) /// The JSON node to evaluate. /// The name of the property. /// The Swift type as a string. + public override string GetType(JsonNode? node, string propertyName) { return node switch { - JsonObject => "[String: Any]", - JsonArray => "[Any]", + JsonObject => propertyName, + JsonArray => $"[{propertyName}Item]", JsonValue value when value.TryGetValue(out _) => "Int", JsonValue value when value.TryGetValue(out _) => "Double", JsonValue value when value.TryGetValue(out _) => "String", diff --git a/src/Riverside.JsonBinder/Serialization/TypeScriptSerializer.cs b/src/Riverside.JsonBinder/Serialization/TypeScriptSerializer.cs index d2eb7db..a697766 100644 --- a/src/Riverside.JsonBinder/Serialization/TypeScriptSerializer.cs +++ b/src/Riverside.JsonBinder/Serialization/TypeScriptSerializer.cs @@ -30,37 +30,39 @@ private void ProcessNode(JsonNode node, string className, List classes) { if (node is JsonObject obj) { - var classDef = $"class {className} {{\n constructor() {{"; + var classDef = $"export class {className} {{"; + var properties = new List(); + var constructorAssignments = new List(); + foreach (var property in obj) { - var propType = GetType(property.Value, property.Key); - classDef += $"\n this.{property.Key} = null;"; + var propType = GetType(property.Value, ToPascalCase(property.Key)); + properties.Add($" {property.Key}: {propType};"); + constructorAssignments.Add($" this.{property.Key} = null;"); } + + classDef += "\n" + string.Join("\n", properties); + classDef += "\n\n constructor() {"; + classDef += "\n" + string.Join("\n", constructorAssignments); classDef += "\n }\n}"; + classes.Add(classDef); foreach (var property in obj) { if (property.Value is JsonObject || property.Value is JsonArray) { - ProcessNode(property.Value, property.Key, classes); + ProcessNode(property.Value, ToPascalCase(property.Key), classes); } } } - else if (node is JsonArray array) + else if (node is JsonArray array && array.Count > 0) { - string elementType = "any"; - if (array.Count > 0) + var firstElement = array[0]; + if (firstElement is JsonObject || firstElement is JsonArray) { - var firstElement = array[0]; - elementType = GetType(firstElement, className); - if (firstElement is JsonObject || firstElement is JsonArray) - { - ProcessNode(firstElement, className + "Item", classes); - elementType = className + "Item"; - } + ProcessNode(firstElement, className + "Item", classes); } - classes.Add($"class {className} {{\n items: {elementType}[];\n\n constructor() {{\n this.items = [];\n }}\n}}"); } } @@ -75,7 +77,7 @@ public override string GetType(JsonNode? node, string propertyName) return node switch { JsonObject => propertyName, - JsonArray => $"{propertyName}[]", + JsonArray => $"{propertyName}Item[]", JsonValue value when value.TryGetValue(out _) => "number", JsonValue value when value.TryGetValue(out _) => "number", JsonValue value when value.TryGetValue(out _) => "string",