using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
public class UnitReference
readonly long unitReferenceId;
public UnitReference(long unitReferenceId)
this.unitReferenceId = unitReferenceId;
public long UnitReferenceId { get { return unitReferenceId; } }
public List<Unit> Units { get; set; }
public string UnitValue { get; set; }
public long Scale { get; set; }
public string Name { get; set; }
public UnitReference UnitReference { get; set; }
public class WidgetConverter : CustomPropertyConverterBase<Widget>
readonly IDictionary<long, UnitReference> units;
public WidgetConverter(IDictionary<long, UnitReference> units)
protected override void ReadCustomProperties(JObject obj, Widget value, JsonSerializer serializer)
var id = (long?)obj.GetValue("UnitReferenceId", StringComparison.OrdinalIgnoreCase);
value.UnitReference = units[id.Value];
protected override bool ShouldSerialize(JsonProperty property, object value)
if (property.UnderlyingName == "UnitReference")
return base.ShouldSerialize(property, value);
protected override void WriteCustomProperties(JsonWriter writer, Widget value, JsonSerializer serializer)
if (value.UnitReference != null)
writer.WritePropertyName("UnitReferenceId");
writer.WriteValue(value.UnitReference.UnitReferenceId);
public abstract class CustomPropertyConverterBase<T> : JsonConverter where T : class
public override bool CanConvert(Type objectType)
return typeof(T).IsAssignableFrom(objectType);
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
if (reader.TokenType == JsonToken.Null)
var jObj = JObject.Load(reader);
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType);
var value = existingValue as T ?? (T)contract.DefaultCreator();
ReadCustomProperties(jObj, value, serializer);
using (var subReader = jObj.CreateReader())
serializer.Populate(subReader, value);
protected abstract void ReadCustomProperties(JObject obj, T value, JsonSerializer serializer);
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(value.GetType());
writer.WriteStartObject();
foreach (var property in contract.Properties.Where(p => ShouldSerialize(p, value)))
var propertyValue = property.ValueProvider.GetValue(value);
if (propertyValue == null && serializer.NullValueHandling == NullValueHandling.Ignore)
writer.WritePropertyName(property.PropertyName);
serializer.Serialize(writer, propertyValue);
WriteCustomProperties(writer, (T)value, serializer);
protected virtual bool ShouldSerialize(JsonProperty property, object value)
return property.Readable && !property.Ignored && (property.ShouldSerialize == null || property.ShouldSerialize(value));
protected abstract void WriteCustomProperties(JsonWriter writer, T value, JsonSerializer serializer);
public static void Test()
var unitsJsonString = GetUnitsJson();
var widgetsJsonString = GetWidgetsJson();
var units = JsonConvert.DeserializeObject<List<UnitReference>>(unitsJsonString)
.ToDictionary(u => u.UnitReferenceId);
var settings = new JsonSerializerSettings
Converters = { new WidgetConverter(units) },
var widgets = JsonConvert.DeserializeObject<List<Widget>>(widgetsJsonString, settings);
var newJson = JsonConvert.SerializeObject(widgets, Formatting.Indented, settings);
Console.WriteLine("Deserialized and re-serialized list of {0}:", typeof(Widget));
Console.WriteLine(newJson);
Assert.IsTrue(JToken.DeepEquals(JToken.Parse(widgetsJsonString), JToken.Parse(newJson)));
var newJson2 = JsonConvert.SerializeObject(widgets, Formatting.Indented);
Console.WriteLine("\nDeserialized and re-serialized list of {0} without using the converter:", typeof(Widget));
Console.WriteLine(newJson2);
static string GetWidgetsJson()
static string GetUnitsJson()
public static void Main()
Console.WriteLine("Environment version: " + Environment.Version);
Console.WriteLine("Json.NET version: " + typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");
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");