using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Runtime.CompilerServices;
using Microsoft.CSharp.RuntimeBinder;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
public static void Main()
Console.WriteLine("Failed with exception: ");
public static void Test()
const string JSON = @"{'Name': 'my first story', 'ToldByUserId': 255, 'EntityName' : 'My Entity Name', 'NestedObject' : {'Foo' : 'Bar' }, 'ExplicitProperty' : 'Explicit Property' }";
var settings = new JsonSerializerSettings
Converters = { new EntityBaseConverter() },
Test<EntityBase>(JSON, settings);
Test<Story>(JSON, settings);
static void Test<T>(string JSON, JsonSerializerSettings settings) where T : EntityBase
dynamic stroy = JsonConvert.DeserializeObject<T>(JSON, settings);
Console.WriteLine("\nDeserialized {0}:", stroy);
var json2 = JsonConvert.SerializeObject(stroy, settings);
Console.WriteLine(json2);
Console.WriteLine("stroy.EntityName: \"{0}\"", stroy.EntityName);
Console.WriteLine("stroy dynamic member names: " + string.Join(",", stroy.GetDynamicMemberNames()));
Console.WriteLine("\nRe-serialized {0}:", stroy);
Console.WriteLine(json2);
Assert.IsTrue(stroy.EntityName == ((string)JObject.Parse(JSON)["EntityName"] ?? ""));
Assert.IsTrue(stroy.ExplicitProperty == ((string)JObject.Parse(JSON)["ExplicitProperty"]));
Assert.IsTrue(!stroy.GetDynamicMemberNames().Contains("EntityName"));
Assert.IsTrue(!stroy.GetDynamicMemberNames().Contains("ExplicitProperty"));
Console.WriteLine("\nAll tests passed.");
public class EntityBaseConverter : ParameterizedDynamicObjectConverterBase<EntityBase>
public override EntityBase CreateObject(JObject jObj, Type objectType, JsonSerializer serializer, ICollection<string> usedParameters)
var entityName = jObj.GetValue("EntityName", StringComparison.OrdinalIgnoreCase);
usedParameters.Add(((JProperty)entityName.Parent).Name);
var entityNameString = entityName == null ? "" : entityName.ToString();
if (objectType == typeof(EntityBase))
return new EntityBase(entityName == null ? "" : entityName.ToString());
return (EntityBase)Activator.CreateInstance(objectType, new object [] { entityNameString });
public abstract class ParameterizedDynamicObjectConverterBase<T> : JsonConverter where T : DynamicObject
public override bool CanConvert(Type objectType) { return typeof(T).IsAssignableFrom(objectType); }
public abstract T CreateObject(JObject jObj, Type objectType, JsonSerializer serializer, ICollection<string> usedParameters);
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
var contract = (JsonDynamicContract)serializer.ContractResolver.ResolveContract(objectType);
if (reader.TokenType == JsonToken.Null)
var jObj = JObject.Load(reader);
var used = new HashSet<string>();
var obj = CreateObject(jObj, objectType, serializer, used);
foreach (var jProperty in jObj.Properties())
var memberName = jProperty.Name;
if (used.Contains(memberName))
JsonProperty property = contract.Properties.GetClosestMatchProperty(memberName);
if (property != null && property.Writable && !property.Ignored)
var propertyValue = jProperty.Value.ToObject(property.PropertyType, serializer);
property.ValueProvider.SetValue(obj, propertyValue);
if (jProperty.Value.Type == JTokenType.Null)
else if (jProperty.Value is JValue)
propertyValue = ((JValue)jProperty.Value).Value;
propertyValue = jProperty.Value.ToObject<IDynamicMetaObjectProvider>(serializer);
CallSiteCache.SetValue(memberName, obj, propertyValue);
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
throw new NotImplementedException();
internal static class CallSiteCache
private static readonly Dictionary<string, CallSite<Func<CallSite, object, object, object>>> setters
= new Dictionary<string, CallSite<Func<CallSite, object, object, object>>>();
public static void SetValue(string propertyName, object target, object value)
CallSite<Func<CallSite, object, object, object>> site;
if (!setters.TryGetValue(propertyName, out site))
var binder = Binder.SetMember(CSharpBinderFlags.None,
propertyName, typeof(CallSiteCache),
new List<CSharpArgumentInfo>{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)});
setters[propertyName] = site = CallSite<Func<CallSite, object, object, object>>.Create(binder);
site.Target(site, target, value);
public class EntityBase : DynamicObject
public string ExplicitProperty { get; set; }
public string EntityName { get; private set; }
private readonly Dictionary<string, object> values = new Dictionary<string, object>();
public EntityBase(string entityName)
this.EntityName = entityName;
public virtual object this[string fieldname]
if (this.values.ContainsKey(fieldname))
return this.values[fieldname];
if (this.values.ContainsKey(fieldname))
this.values[fieldname] = value;
this.values.Add(fieldname, value);
public override IEnumerable<string> GetDynamicMemberNames()
return this.values.Keys.ToList();
public override bool TryGetMember(GetMemberBinder binder, out object result)
result = this[binder.Name];
public override bool TrySetMember(SetMemberBinder binder, object value)
this[binder.Name] = value;
public class Story : EntityBase
public Story(string entityName) : base(entityName) { }
public class AssertionFailedException : System.Exception
public AssertionFailedException() : base() { }
public AssertionFailedException(string s) : base(s) { }
public static class Assert
public static void IsTrue(bool value)
public static void IsTrue(bool value, string message)
throw new AssertionFailedException(message ?? "failed");