165 lines
5.2 KiB
C#
165 lines
5.2 KiB
C#
using DevDisciples.Json.Parser;
|
|
using DevDisciples.Json.Parser.Syntax;
|
|
using DevDisciples.Parsing;
|
|
using Humanizer;
|
|
|
|
namespace DevDisciples.Json.Tools;
|
|
|
|
public static partial class Json2CSharpTranslator
|
|
{
|
|
private static readonly VisitorContainer<IJsonSyntax, Context, ITranslation> Visitors = new();
|
|
|
|
static Json2CSharpTranslator()
|
|
{
|
|
Visitors.Register<JsonObjectSyntax>(Object);
|
|
Visitors.Register<JsonArraySyntax>(Array);
|
|
Visitors.Register<JsonStringSyntax>(String);
|
|
Visitors.Register<JsonNumberSyntax>(Number);
|
|
Visitors.Register<JsonBoolSyntax>(Bool);
|
|
Visitors.Register<JsonNullSyntax>(Null);
|
|
}
|
|
|
|
public static string Translate(string source, Context? context = null)
|
|
{
|
|
if (JsonParser.Parse("<source>", source) is not JsonObjectSyntax root)
|
|
throw new ParsingException("Expected a JSON object.");
|
|
|
|
context ??= new();
|
|
context.CurrentName = context.RootClassName;
|
|
|
|
var visitor = Visitors[typeof(JsonObjectSyntax)];
|
|
visitor(root, context);
|
|
|
|
context.Builder.Append("//using System;\n");
|
|
context.Builder.Append("//using System.Collections.Generic;\n");
|
|
context.Builder.Append('\n');
|
|
context.Builder.Append($"namespace {context.Namespace};\n\n");
|
|
context.Classes.ForEach(@class => @class.Translate(context));
|
|
|
|
return context.Builder.ToString();
|
|
}
|
|
|
|
private static ITranslation Object(IJsonSyntax visitee, Context context)
|
|
{
|
|
var @object = (JsonObjectSyntax)visitee;
|
|
var @class = new ClassTranslation
|
|
{
|
|
Name = context.CurrentName,
|
|
Properties = new()
|
|
};
|
|
|
|
context.Classes.Add(@class);
|
|
|
|
foreach (var prop in @object.Properties)
|
|
{
|
|
context.CurrentName = prop.Key.Lexeme;
|
|
var visitor = Visitors[prop.Value.GetType()];
|
|
var translation = visitor(prop.Value, context);
|
|
|
|
switch (translation)
|
|
{
|
|
case ClassTranslation:
|
|
@class.Properties.Add(new PropertyTranslation
|
|
{
|
|
Type = translation.Name,
|
|
Name = translation.Name,
|
|
});
|
|
break;
|
|
case PropertyTranslation property:
|
|
@class.Properties.Add(property);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return @class;
|
|
}
|
|
|
|
private static ITranslation Array(IJsonSyntax visitee, Context context)
|
|
{
|
|
var array = (JsonArraySyntax)visitee;
|
|
var type = "object";
|
|
|
|
if (array.Elements.All(e => e is JsonObjectSyntax))
|
|
{
|
|
context.Classes.Add(SquashObjects(context.CurrentName, array.Elements, context));
|
|
type = context.Classes.Last().Name;
|
|
}
|
|
else if (array.Elements.All(e => e is JsonStringSyntax es && DateTime.TryParse(es.Value, out _)))
|
|
{
|
|
type = "DateTime";
|
|
}
|
|
else if (array.Elements.All(e => e is JsonStringSyntax))
|
|
{
|
|
type = "string";
|
|
}
|
|
else if (array.Elements.All(e => e is JsonNumberSyntax))
|
|
{
|
|
type = array.Elements.Any(e => ((JsonNumberSyntax)e).Token.Lexeme.Contains('.')) ? "double" : "int";
|
|
}
|
|
|
|
return new PropertyTranslation
|
|
{
|
|
Type = $"List<{type}>",
|
|
Name = context.CurrentName,
|
|
};
|
|
}
|
|
|
|
private static ITranslation String(IJsonSyntax visitee, Context context)
|
|
{
|
|
var @string = (JsonStringSyntax)visitee;
|
|
var type = DateTime.TryParse(@string.Value, out _) ? "DateTime" : "string";
|
|
|
|
return new PropertyTranslation
|
|
{
|
|
Type = type,
|
|
Name = context.CurrentName,
|
|
};
|
|
}
|
|
|
|
private static ITranslation Number(IJsonSyntax visitee, Context context)
|
|
{
|
|
return new PropertyTranslation
|
|
{
|
|
Type = ((JsonNumberSyntax)visitee).Token.Lexeme.Contains('.') ? "double" : "int",
|
|
Name = context.CurrentName,
|
|
};
|
|
}
|
|
|
|
private static ITranslation Bool(IJsonSyntax visitee, Context context)
|
|
{
|
|
return new PropertyTranslation
|
|
{
|
|
Type = "bool",
|
|
Name = context.CurrentName,
|
|
};
|
|
}
|
|
|
|
private static ITranslation Null(IJsonSyntax visitee, Context context)
|
|
{
|
|
return new PropertyTranslation
|
|
{
|
|
Type = "object",
|
|
Name = context.CurrentName,
|
|
};
|
|
}
|
|
|
|
private static ClassTranslation SquashObjects(string className, IEnumerable<IJsonSyntax> objects, Context context)
|
|
{
|
|
var classes = objects
|
|
.Select(@object => Object(@object, context))
|
|
.ToArray();
|
|
|
|
var squashed = new ClassTranslation
|
|
{
|
|
Name = className.Singularize(),
|
|
Properties = new()
|
|
};
|
|
|
|
foreach (var @class in classes)
|
|
foreach (var prop in ((ClassTranslation)@class).Properties)
|
|
if (squashed.Properties.All(p => p.Name != prop.Name))
|
|
squashed.Properties.Add(prop);
|
|
|
|
return squashed;
|
|
}
|
|
} |