Skip to content

Commit

Permalink
fix bug when generating classes when root node is a list, causing mul…
Browse files Browse the repository at this point in the history
…tiple unwated classes getting generated. +improved support for lists
  • Loading branch information
NoobNotFound committed Jan 18, 2025
1 parent a7f0e5d commit bc1bd39
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 140 deletions.
23 changes: 8 additions & 15 deletions src/Riverside.JsonBinder/Serialization/CSharpSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ private void ProcessNode(JsonNode node, string className, List<string> 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);
Expand All @@ -43,24 +43,17 @@ private void ProcessNode(JsonNode node, string className, List<string> 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}}");
}
}

Expand All @@ -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<int>(out _) => "int",
JsonValue value when value.TryGetValue<double>(out _) => "double",
JsonValue value when value.TryGetValue<string>(out _) => "string",
Expand Down
46 changes: 28 additions & 18 deletions src/Riverside.JsonBinder/Serialization/JavaScriptSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,40 +26,50 @@ public override string GenerateClasses(JsonNode node, string className)
/// <param name="node">The JSON node to process.</param>
/// <param name="className">The name of the class to generate.</param>
/// <param name="classes">The list of generated class definitions.</param>

private void ProcessNode(JsonNode node, string className, List<string> classes)
{
if (node is JsonObject obj)
{
var classDef = $"class {className} {{\n constructor() {{";
var classDef = $"class {className} {{";
var constructorAssignments = new List<string>();

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

Expand All @@ -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<int>(out _) => "number",
JsonValue value when value.TryGetValue<double>(out _) => "number",
JsonValue value when value.TryGetValue<string>(out _) => "string",
JsonValue value when value.TryGetValue<bool>(out _) => "boolean",
_ => "any"
_ => "*"
};
}
}
35 changes: 17 additions & 18 deletions src/Riverside.JsonBinder/Serialization/JavaSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,38 +31,37 @@ private void ProcessNode(JsonNode node, string className, List<string> classes)
if (node is JsonObject obj)
{
var classDef = $"public class {className} {{";
var fields = new List<string>();
var methods = new List<string>();

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

Expand All @@ -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<int>(out _) => "int",
JsonValue value when value.TryGetValue<double>(out _) => "double",
JsonValue value when value.TryGetValue<string>(out _) => "String",
Expand Down
13 changes: 13 additions & 0 deletions src/Riverside.JsonBinder/Serialization/LanguageSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,17 @@ public abstract class LanguageSerializer
/// <param name="propertyName">The name of the property.</param>
/// <returns>The type as a string.</returns>
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()));
}
}
40 changes: 23 additions & 17 deletions src/Riverside.JsonBinder/Serialization/PHPSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,36 +30,42 @@ private void ProcessNode(JsonNode node, string className, List<string> classes)
{
if (node is JsonObject obj)
{
var classDef = $"class {className} {{\n";
var classDef = $"class {className}\n{{";
var properties = new List<string>();
var methods = new List<string>();

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

Expand All @@ -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<int>(out _) => "int",
JsonValue value when value.TryGetValue<double>(out _) => "float",
JsonValue value when value.TryGetValue<string>(out _) => "string",
JsonValue value when value.TryGetValue<bool>(out _) => "bool",
_ => "mixed"
_ => "mixed" // PHP 8+ type hint
};
}
}
43 changes: 23 additions & 20 deletions src/Riverside.JsonBinder/Serialization/PythonSerializer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Text.Json.Nodes;
using System.Linq;
using System.Text.Json.Nodes;

namespace Riverside.JsonBinder.Serialization;

Expand Down Expand Up @@ -26,43 +27,45 @@ public override string GenerateClasses(JsonNode node, string className)
/// <param name="node">The JSON node to process.</param>
/// <param name="className">The name of the class to generate.</param>
/// <param name="classes">The list of generated class definitions.</param>
///

private void ProcessNode(JsonNode node, string className, List<string> 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<string>();

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}] = []");
}
}


/// <summary>
/// Gets the Python type for a given JSON node.
/// </summary>
Expand All @@ -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<int>(out _) => "int",
JsonValue value when value.TryGetValue<double>(out _) => "float",
JsonValue value when value.TryGetValue<string>(out _) => "str",
JsonValue value when value.TryGetValue<bool>(out _) => "bool",
_ => "Any"
_ => "object"
};
}
}
Loading

0 comments on commit bc1bd39

Please sign in to comment.