65.9K
CodeProject is changing. Read more.
Home

C# Filtering with JSON

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.29/5 (6 votes)

Sep 16, 2010

CPOL

1 min read

viewsIcon

47857

downloadIcon

609

Part 2 of creating a dynamical filtering mechanism in C#

filter.jpg

Introduction

In the first part, I presented a small JSON object model that can be used as a way to preserve the structure of a filter (used by jqGrid). In this second part, we'll take the model and convert it to a server-side filter.

Using the Code

So here are the entities behind the design:

public class Group
{
    public GroupOperator Operator { get; set; }

    public List<Rule> Rules { get; set; }

    public List<Group> Groups { get; set; }
}
	
public class Rule
{
    public string Field { get; set; }

    public RuleOperator Operator { get; set; }

    public string Data { get; set; }
}

Using these classes, we can replicate any filter created on the client side. In terms of LINQ, the Rule is equivalent to a lambda expression and the Group class is equivalent to an expression tree. We can therefore write the methods that translate the model into System.Linq.Expressions.Expression:

From Rule to lambda expression:

public Expression ToExpression(Type parameterType, ParameterExpression param) {
    // get the property that will be evaluated
    PropertyInfo pi = null;
    MemberExpression member = null;
    
    if (this.Field.Contains(".")) // check for subproperties
    {
        ....
    }
    else {
        pi = parameterType.GetProperty(this.Field);
        member = Expression.PropertyOrField(param, this.Field);
    }
    
    ConstantExpression constant = this.Operator == RuleOperator.IsNull || 
	this.Operator == RuleOperator.NotNull
            ? Expression.Constant(null, pi.PropertyType)
            : Expression.Constant(this.CastDataAs(pi.PropertyType), pi.PropertyType);
            
    switch(this.Operator) {
        case RuleOperator.IsNull: // it's the same for null
        case RuleOperator.Equals:
             return Expression.Equal(member, constant);
        ....
    }
    
    return null;
}

From Group to expression tree:

    public Expression<func><t, bool> ToExpressionTree<t>(){
        Type t = typeof(T);
        
        // create a parameter expression that can be passed to the rules
        ParameterExpression param = Expression.Parameter(t, "p");
        
        // get the expression body. This consists of all subgroups and rules
        Expression body = GetExpressionFromSubgroup(this, t, param);
        
        if (body == null)
            return null;
        else 
            return Expression.Lambda<func<t, bool>>(
                            body,
                            new ParameterExpression[] { param }
                       );
    }
    
    protected Expression GetExpressionFromSubgroup
	(Group subgroup, Type parameterType,ParameterExpression param) {
        Expression body = null;
        .....
        return body;
    }
}

I built a small helper called WebHelper to help me with the serialization and deserialization. To put it all in one, I also added the JSONToExpressionTree method that converts the serialized JSON into an expression tree.

So let's run some tests to see if it all goes well! We will need a simple domain:

public class Country {
    public Country() { }
    
    public int Id { get; set; }
    public string Name { get; set; }
    
    public Continent Continent { get; set; }
}

public class Continent {
    public Continent() { }
    
    public string Name { get; set; }
    
    public IList<country> Countries { get; set; }
}

And a filter:

Group g = new Group() { Operator = GroupOperator.And };
g.Rules.Add(new Rule() { Field = "Continent.Name", 
	Operator = RuleOperator.StartsWith, Data = "E" });

Conclusion

Hope you enjoyed this. The next article will merge the two in an easy to use application.

Link

Part 1 of creating a dynamical filtering mechanism in C#

History

  • 16th September, 2010: Initial post