Skip to content

A Json generator engine based on the metadata including layout and data providers defined in .Net languages.

License

Notifications You must be signed in to change notification settings

mkokabi/JsonGen

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

68 Commits
 
 
 
 
 
 
 
 

Repository files navigation

JsonGen

A Json generator engine based on the metadata including layout and data providers defined in .Net languages. Usually, if a specific Json needs to be created based on a defined layout, developer has to create all the classes and hierarchy and then map his data to this structure, and then finally serialize his root class to get the Json. JsonGen is following another appraoch. The steps is: tag the Json layout with _datasource and then define your datasource(s). Finally, ask JsonGen to generate the finall Json with the data.

Builds

Build on VSTS

Sample

Let's start with the simplest form (https://dotnetfiddle.net/hNKHYQ):

public class DataProvider : IDataProvider
{
   public async Task<IEnumerable<dynamic>> GetDataAsync() => 
	  await Task.FromResult((new dynamic[]
		{
		  1, 2, 3
		}).AsEnumerable());
}

var generator = new Generator(new BasicMetadataProvider(name => 
	new Metadata
		{
		  DataSources = new[]
		  {
			new DataSource {
			  Key = "W",
			  DataProviderFullName = typeof(DataProvider).FullName
			}
		  },
		  Labels = new Labels { },
		  Layout = new Layout { Content = "{'_dataSource':'W', 'data':[]}" }
		}));
var generatedJson = generator.Generate("MyMetadata");

The output will be

{
  '_dataSource': 'W',
  'data': [1, 2, 3]
};

or another way with setting fields

var metadataProvider = new BasicMetadataProvider(name => new Metadata
  {
    DataSources = new[]
    {
      new DataSource {
      Key = "W",
      DataProviderFullName = typeof(DataProvider).FullName
    }
  },
  Labels = new Labels { },
  Layout = new Layout { 
	Content = "{'_dataSource':'W', 'data':[{'field1': '', 'field2': ''}]}" }
  }
);

public class DataProvider : IDataProvider
{
  public IEnumerable<dynamic> GetData() => new[]
  {
    new { field1 ="Hello",  field2 = "World" }
  };
}

[TestMethod]
public void Simple_Test()
{
  var generator = new Generator(metadataProvider);
  var generatedJson = generator.Generate("SomeMetadata");
  var expected = @"{
        '_dataSource': 'W',
        'data': [
          {
            'field1': 'Hello',
            'field2': 'World'
          }
        ]
      }";  
}
  }



Metadata:

Metadata class contains 3 main components: Layout, Datasources and Labels

Layout

Simply it holds the content as a Json

public class Layout
{
    public string Content { get; set; }
}

In this Json the _datasource element is pointing to the key of a datasource. The first array element sibling to this node is where the data is going to be placed.

Datasources

Another important part of Metadata is Datasources which is an array of Datasource. The main element of datasource is the Key which is the reference used in the layout. the next important part is DataProviderFullName which is the full name of a class implementing IDataProvider

IDataProvider

This interface must be implemented by any data provider and has one simple method GetData which returns an IEnumerable of dynamic.

public interface IDataProvider
{
    Task<IEnumerable<dynamic>> GetDataAsync();
}

There is a DbDataProvider in JsonGen.Db project which can be used on the databases.

Labels

To ba added



Sepcial features

Filtering

For filtering, Generate method can accept one of these 2 optional parameters: predicate or filters. predicate is a condition which can filter the datasource rows. On the other hand filters parameter is used for database sources and basically is a list of Filter objects which have name, value and optionally operator.

note : to use the predicate the data provider should be implementing IFilterableDataProvider and for using filters it should be implementing IDbDataProvider.

Supported filter operators are:

  • Eq: Equals
  • G: Greater than
  • L: Less than
  • GE: Greater than or equal to
  • LE: Less than or equal to
  • In: In a set values. Value should be an array

Sample

var metadataProvider = new BasicMetadataProvider(_ => new Metadata
{
    Layout = new Layout { 
       Content = "{'_dataSource': 'A', 'data': [ { 'name': 'X' }]}" 
    },
    Labels = new Labels(),
    DataSources = new[]
    {
        new DataSource {
            Key = "A",
            DataProviderFullName = typeof(DbDataProvider).FullName,
            DbConnection = new SqlConnection(connStr),
            Query = "Select * from TestTable",
        }
    }
});

var generator = new Generator(metadataProvider);
var json = await generator.GenerateAsync("myMeta", 
	filters: new[] 
		{ new Filter { FieldName = "Id", Value = 1 } }
	);

The output would be:

{'_dataSource': 'A', 'data': [ { 'Name': 'MK' }]}

Excluding from filtering

When there are multiple data sources used in one layout and some of them shouldn't be filtered that data source can use DatasourceOptions of ApplyFilter = false.

new DataSource {
    Key = "A",
    DataProviderFullName = typeof(DbDataProvider).FullName,
    DbConnection = new SqlConnection(connStr),
    Query = "Select * from TestTable",
    Options = new DatasourceOptions { ApplyFilter = false }
}

In more specific cases, a set of filters based on their key can be ignored:

Options = new DatasourceOptions { 
    IgnoreFilteringOn = new [] { "id", "Name" } 
}

Stored procedures

For calling stored procedure instead of query should start with Exec (case insensitive). In this case, filters would be passed as parameters to the stored procedure:

Sample

CREATE PROCEDURE [dbo].[TestProc]
	@param1 int = 0,
	@param2 VARCHAR(10)
AS
	SELECT * FROM dbo.TestTable WHERE Id = @param1 OR Name = @param2
RETURN 0

var metadataProvider = new BasicMetadataProvider(_ => new Metadata
{
  Layout = new Layout { Content = "{'_dataSource': 'A', 'data': [ { 'Name': 'X' }]}" },
  Labels = new Labels(),
  DataSources = new[]
  {
    new DataSource {
      Key = "A",
      DataProviderFullName = typeof(DbDataProvider).FullName,
      DbConnection = new SqlConnection(connStr),
      Query = " eXec TestProc",
    }
  }
});

var generator = new Generator(metadataProvider);
var json = await generator.GenerateAsync("myMeta", filters: new[] {
  new Filter {
    FieldName = "param1",
    Value = 1,
  },
  new Filter {
    FieldName = "param2",
    Value = "AK",
  }});

Scalar

The data provider can be retunring scalar values instead of a list of data. For these scenarios the DataProvider should be implementing IScalarDataProvider.

public interface IScalarDataProvider: IDataProvider
{
    Task<dynamic> GetScalarDataAsync();
}

For data sources connecting to databases ScalarDbDataProvider can be used.

Sample:

var metadataProvider = new BasicMetadataProvider(_ => new Metadata
{
    Layout = new Layout { Content = "{'_dataSource': 'A', 'data': 'x' }" },
    Labels = new Labels(),
    DataSources = new[]
    {
        new DataSource {
            Key = "A",
            DataProviderFullName = typeof(ScalarDbDataProvider).FullName,
            DbConnection = new SqlConnection(connStr),
            Query = "Select Max(Id) from TestTable",
        }
    }
});

var generator = new Generator(metadataProvider);

The ouput would be

{'_dataSource': 'A', 'data': 2 }

SimpleRow data provider

sometime the elements are not sitting in an array like:

{'Id':1, 'Name': 'Mohsen'}

for these scenario the single row data provider could be used.

Task<dynamic> GetSingleRowDataAsync(Filter[] filters);

Then the layout can be set as:

public class SingleRowDataProvider : ISingleRowDataProvider
{
    public async Task<dynamic> GetSingleRowDataAsync(Filter[] filters) => await Task.FromResult(new Data
    {
        X = 1,
        Y = 2,
        Z = 3
    });
}

var layout = @"{
'root': {
    'node': {
        '_dataSource': 'S',
        'X': '',
        'Y': '',
        'Z': ''
        }
    }
} ";
var metadataProvider = new BasicMetadataProvider(_ => new Metadata
{
    Layout = new JsonGen.Layout { Content = layout },
    DataSources = new[] {
        new DataSource { DataProviderFullName = typeof(SingleRowDataProvider).FullName,
            Key = "S"
        }
    }
});

replacing macros

In scenarios where dynamic sql is neccessary to be used, the replacing keys should be specified in the [ ] and the filter options should be set to ReplaceMacrosOnly . The keys should be passed as normal filter and their value be replaced.

sample

DataSources = new[]
  {
    new DataSource {
      Key = "A",
      DataProviderFullName = typeof(DbDataProvider).FullName,
      DbConnection = new SqlConnection(connStr),
      Query = "Select * from [TableName]",
      Options = new DatasourceOptions { 
        ReplaceMacrosOnly = true 
      }
  }
}

var json = await generator.GenerateAsync("myMeta", 
    filters: new[] { new Filter { FieldName = "TableName", Value = "TestTable" } });

Please note this replacement is text based only so if it's used for a string value the quotes should be set around [].

parameters

The other parameter which can be passed to the Generate method is parameters which is simply a dictionary of named objects. Based on these parameters, the data source could be switched at run time.

sample

var basicMetadataProvider = new BasicMetadataProvider(_ => 
    new Metadata
    {
      Labels = new Labels(),
      Layout = new Layout { Content = "{'_dataSource':'ds_[p]', 'data':[]}" },
      DataSources = new[]
        {
          new DataSource { Key = "ds_A", DataProviderFullName = typeof(DataProviderA).FullName },
          new DataSource { Key = "ds_B", DataProviderFullName = typeof(DataProviderB).FullName },
        }
    });

var generator = new Generator(basicMetadataProvider);
var json = generator.Generate(
    "MetadataName", 
    parameters: new Dictionary<string, dynamic> { { "p", "B" } }
);

At run time the parameter p would be changed with its value of B therefore datasource ds_B would be used:

{'_dataSource':'ds_[p]', 'data':[4, 5, 6]}

(https://dotnetfiddle.net/ixEcfo)

About

A Json generator engine based on the metadata including layout and data providers defined in .Net languages.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published