using System.Linq.Expressions;
using System.Text.RegularExpressions;
using System.Collections.Generic;
public static void Main()
Console.WriteLine("Hello World");
var doc = System.Text.Json.JsonSerializer.Deserialize<JsonElement>(Sample);
var x = new JsonExpressionParser().ParsePredicateOf<int>(doc);
static string Sample = """
"Name": "Rule_Contains_Carrot",
"Name": "Rule_Contains_Carrot",
"Name": "Rule_Contains_Carrot",
"TwinRelationships": null,
"ParentId": "Pumpestationer",
"Name": "Rule_Contains_Carrot",
"TwinRelationships": null,
"ParentId": "Pumpestationer",
"Name": "Rule_Contains_Carrot",
"TwinRelationships": null,
"SourceParentExpression": null,
"SourceExpression": null,
"SourceParentExpression": null,
"SourceExpression": null,
"SourceParentExpression": null,
"SourceExpression": null,
public class JsonExpressionParser
private const string StringStr = "string";
private readonly string BooleanStr = nameof(Boolean).ToLower();
private readonly string Number = nameof(Number).ToLower();
private readonly string In = nameof(In).ToLower();
private readonly string And = nameof(And).ToLower();
private readonly string Contains = nameof(Contains).ToLower();
private readonly MethodInfo MethodContains = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public).Single(m => m.Name == nameof(Enumerable.Contains) && m.GetParameters().Length == 2);
private readonly MethodInfo PerformMyMeth = typeof(Regex).GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault(m => m.Name.ToString() == nameof(Regex.IsMatch) && m.GetParameters().Length == 2);
private delegate Expression Binder(Expression left, Expression right);
private Expression ParseTree<T>(JsonElement condition, ParameterExpression parm)
var gate = condition.GetProperty("Condition").GetString();
JsonElement Rules = condition.GetProperty(nameof(Rules));
Binder binder = gate == And ? (Binder)Expression.And : Expression.Or;
Expression bind(Expression left, Expression right) => left == null ? right : binder(left, right);
string valueConcatenated = string.Empty;
foreach (var rule in Rules.EnumerateArray())
if (rule.TryGetProperty("Condition", out JsonElement check))
var right = ParseTree<T>(rule, parm);
left = bind(left, right);
string @Operator = rule.GetProperty(nameof(@Operator)).GetString();
string @Type = rule.GetProperty(nameof(@Type)).GetString();
string @Field = rule.GetProperty(nameof(@Field)).GetString();
JsonElement @Value = rule.GetProperty(nameof(@Value));
valueConcatenated += @Value.ToString();
var propertyInfo = typeof(IDictionary<string, object>).GetProperty("Item");
var arguments = new List<Expression>{Expression.Constant(@Field)};
var property = Expression.Convert(Expression.MakeIndex(parm, propertyInfo, arguments), (@Type == StringStr || @Type == BooleanStr) ? typeof(string) : typeof(Decimal));
var contains = MethodContains.MakeGenericMethod(typeof(string));
object val = @Value.EnumerateArray().Select(e => e.GetString()).ToList();
var right = Expression.Call(contains, Expression.Constant(val), property);
left = bind(left, right);
else if (@Operator == Contains)
object val = valueConcatenated;
var toCompare = Expression.Constant(val);
var right = Expression.Call(PerformMyMeth, property, toCompare);
left = bind(left, right);
object val = (@Type == StringStr || @Type == BooleanStr) ? (object)@Value.GetString() : @Value.GetDecimal();
var toCompare = Expression.Constant(val);
var right = Expression.Equal(property, toCompare);
left = bind(left, right);
public Expression<Func<IDictionary<string, object>, bool>> ParseExpressionOf<T>(JsonElement doc)
var itemExpression = Expression.Parameter(typeof(IDictionary<string, object>), "valueBag");
var conditions = ParseTree<T>(doc, itemExpression);
if (conditions.CanReduce)
conditions = conditions.ReduceAndCheck();
Console.WriteLine("Conditions: " + conditions.ToString());
var query = Expression.Lambda<Func<IDictionary<string, object>, bool>>(conditions, itemExpression);
public Func<IDictionary<string, object>, bool> ParsePredicateOf<T>(JsonElement doc)
var query = ParseExpressionOf<T>(doc);