In this post, you will find an introduction to the internal workings of BootGen, the code generator that can create ASP.NET 5 and Vue 3 application based on a JSON data set.
Introduction
{
"users": [
{
"userName": "Jon",
"email": "jon@arbuckle.com",
"pets": [
{
"name": "Garfield",
"species": "cat"
},
{
"name": "Odie",
"species": "dog"
}
]
}
]
}
Imagine that you are starting to write a web application for pet owners. The piece of JSON code above is your initial data set. How do you start it? Before you can start implementing the hot features you have imagined for this project, there is some groundwork to be done first.
For the database, you will need two tables, users
and pets
. You will need a seed that fills up your database with the initial dataset
. For the backend, you will need data services and controllers for the user
and the pet
class. For the frontend, you will need a REST API client and state management. Some basic viewer and editor will also surely come in handy. Oh, and do not forget the authentication!
Now that you are done with all these, you can start working on the meaningful part. But how much work does this "groundwork" take? Well, if your chosen stack is ASP.NET 5 with Vue 3 and TypeScript, it will be 1677 lines of code in 26 files. This amount of code does not include the empty project created with the dotnet new
and the vue create
commands.
The goal of the BootGen project is to take a JSON dataset
and generate the groundwork for your project, saving you hours or even days of work.
You can already try this at bootgen.com!
How Does It Work?
The point of this article is to discuss how BootGen works under the hood. It is not necessary to understand this if you only wish to use the tool and enjoy the freed-up time. But those who would like to gain a deeper understanding, please stay with me.
Creating the Data Model
There are two important steps in the generation process. First, a data model should be built based on the data set. The following classes can be used to create the data model.
public class ClassModel
{
public int Id { get; set; }
public string Name { get; set; }
public List<Property> Properties { get; }
}
public class Property
{
public string Name { get; set; }
public BuiltInType BuiltInType { get; set; }
public bool IsCollection { get; set; }
public ClassModel Class { get; set; }
}
public enum BuiltInType { String, Int, Float, Bool, DateTime, Object }
These are simplified from what we actually use in the SDK. If you are interested in the complete meta-model, you can find it at https://github.com/BootGen/BootGenSDK.
The names of the classes can be easily inferred from the JSON property names if we assume that the collection names are always in the plural form and everything else is always in the singular form. Pluralize.NET enables us to conveniently find the plural or the singular form of a particular word.
For each JSON property, we will create a property in our model. If it has a primitive type (string
, integer
, float
, boolean
, or date-time
), then we are done in one step. However, if it is an object
, then we continue recursively: we will check if a class model with the same name already exists. If yes, we will extend it, if not, then we create a new one.
Rendering the Code
To render the code, we use a templating language called Scriban. The simplest template that generates a C# entity class would look like this:
public class {{ class.name }}
{
{{~ for property in class.properties ~}}
public {{ get_type property }} {{ property.name }} { get; set; }
{{~ end ~}}
}
The class variable refers to an object of ClassModel type. In Scriban, every property and function name is converted to snake case. The get_type
is a function call that is implemented in C# and looks like this:
public static string GetType(Property property)
{
string baseType = GetBaseType(property);
if (property.IsCollection)
return $"List<{baseType}>";
return baseType;
}
public static string GetBaseType(Property property)
{
switch (property.BuiltInType)
{
case BuiltInType.Bool:
return "bool";
case BuiltInType.Float:
return "float";
case BuiltInType.String:
return "string";
case BuiltInType.DateTime:
return "DateTime";
case BuiltInType.Object:
return property.Class.Name;
default:
return "int";
}
}
When generating TypeScript code, a different GetType
function is used.
Project Structure
BootGen SDK
This is the core library that handles building the data model from the JSON input and rendering the Scriban templates.
Framework Plugins
The support for different frameworks is implemented as plugins. Currently, the following plugins are implemented:
A framework plugin contains:
- a bunch of Scriban templates
- some static files (basically an empty project for the given framework)
- a configuration file
We do our best to keep the plugin interface clean, to make it easy to implement support for other frameworks in the future.
Web Application
This is the web application deployed to bootgen.com. A convenient way to use BootGen.
History
- 9th August 2021: First version