using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Collections.ObjectModel;
using System.Linq.Expressions;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
public class ExpresssionDTO<TTable> : BaseDTO where TTable : class
[JsonProperty(ItemConverterType = typeof(GenericFuncExpressionArgumentConverterDecorator), ItemConverterParameters = new object [] { typeof(PredicateSerializationConverter<>) })]
public ICollection<Expression<Func<TTable, bool>>> Predicates { get; set; } = new List<Expression<Func<TTable, bool>>>();
[JsonConverter(typeof(GenericFuncExpressionArgumentConverterDecorator), new object [] { typeof(FilterSerializationConverter<>) })]
public Expression<Func<TTable, object>> Filter { get; set; } = null;
public class PredicateSerializationConverter<TTable> : ExpressionSerializer<TTable, bool> where TTable : class
public PredicateSerializationConverter() :base()
public class FilterSerializationConverter<TTable> : ExpressionSerializer<TTable, object> where TTable : class
public FilterSerializationConverter() : base()
public class ExpressionSerializer<T, U> : JsonConverter where T : class
public override bool CanConvert(Type objectType) => throw new NotImplementedException();
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
throw new NotImplementedException();
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
var f = (Expression<Func<T, U>>)value;
writer.WriteValue(f.ToString());
public class GenericFuncExpressionArgumentConverterDecorator : JsonConverter
readonly Type openGenericConverterType;
volatile Tuple<Type, JsonConverter> converterCache;
public GenericFuncExpressionArgumentConverterDecorator(Type openGenericConverterType)
if (openGenericConverterType == null)
throw new ArgumentNullException();
if (!openGenericConverterType.IsSubclassOf(typeof(JsonConverter)))
throw new ArgumentException(string.Format("{0} is not a JsonConvreter", GetType().Name));
if (!openGenericConverterType.IsGenericTypeDefinition)
throw new ArgumentException(string.Format("{0} is not an open generic type", GetType().Name));
this.openGenericConverterType = openGenericConverterType;
public override bool CanConvert(Type objectType) =>
throw new NotImplementedException(string.Format("{0} is intended to be applied via a JsonConverter or JsonProperty attribute", GetType().Name));
JsonConverter GetConverter(Type objectType)
var cache = converterCache;
if (cache != null && cache.Item1 == objectType)
var expressionType = objectType.BaseTypesAndSelf().Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Expression<>)).FirstOrDefault();
if (expressionType == null)
throw new JsonSerializationException(string.Format("Invalid expression type {0}", objectType));
var delegateType = objectType.GetGenericArguments().Single();
if (!delegateType.IsGenericType || delegateType.GetGenericTypeDefinition() != typeof(Func<,>))
throw new JsonSerializationException(string.Format("Invalid delegate type {0}", delegateType));
var argType = delegateType.GetGenericArguments()[0];
var converterType = openGenericConverterType.MakeGenericType(new [] { argType });
var converter = (JsonConverter)Activator.CreateInstance(converterType);
converterCache = Tuple.Create(objectType, converter);
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) =>
GetConverter(objectType).ReadJson(reader, objectType, existingValue, serializer);
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) =>
GetConverter(value.GetType()).WriteJson(writer, value, serializer);
public static class TypeExtensions
public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
static bool TestPredicate(string s)
public static void Test()
var dto = new ExpresssionDTO<string>
(s) => s.Contains("hello"),
(s) => !string.IsNullOrEmpty(s),
(s) => TestPredicate(s + " "),
Filter = (s) => (object)s.Length,
var json = JsonConvert.SerializeObject(dto, Formatting.Indented);
public static void Main()
Console.WriteLine("Environment version: {0} ({1})", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , GetNetCoreVersion());
Console.WriteLine("{0} version: {1}", typeof(JsonSerializer).Assembly.GetName().Name, typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");
public static string GetNetCoreVersion()
var assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly;
var assemblyPath = assembly.Location.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App");
if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2)
return assemblyPath[netCoreAppIndex + 1];