using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using System.Diagnostics;
public class ArrayReferencePreservngConverter : JsonConverter
const string refProperty = "$ref";
const string idProperty = "$id";
const string valuesProperty = "$values";
public override bool CanConvert(Type objectType)
if (objectType == typeof(byte[]))
return objectType.IsArray && objectType.GetArrayRank() == 1;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
Debug.Assert(CanConvert(objectType));
if (reader.TokenType == JsonToken.Null)
else if (reader.TokenType == JsonToken.StartArray)
var elementType = objectType.GetElementType();
var listType = typeof(List<>).MakeGenericType(elementType);
var list = (IList)serializer.Deserialize(reader, listType);
var array = Array.CreateInstance(elementType, list.Count);
var obj = JObject.Load(reader);
var refId = (string)obj[refProperty];
var reference = serializer.ReferenceResolver.ResolveReference(serializer, refId);
var values = obj[valuesProperty];
if (values == null || values.Type == JTokenType.Null)
throw new JsonSerializationException(string.Format("{0} was not an array", values));
var count = ((JArray)values).Count;
var elementType = objectType.GetElementType();
var array = Array.CreateInstance(elementType, count);
var objId = (string)obj[idProperty];
serializer.ReferenceResolver.AddReference(serializer, objId, array);
var listType = typeof(List<>).MakeGenericType(elementType);
using (var subReader = values.CreateReader())
var list = (IList)serializer.Deserialize(subReader, listType);
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
throw new NotImplementedException();
public static void Test()
Console.WriteLine("Testing recursive array reference resolution... ");
Console.WriteLine("Testing non-recursive array reference resolution... ");
Console.WriteLine("Pass");
public static void TestSelfReference()
var o = new Test { Tests = new[] { new Test(), new Test() } };
var arr = new[] { o, o };
arr[0].Tests[0].Tests = arr;
static void TestNoSelfReference()
var o = new Test { Tests = new[] { new Test(), new Test() } };
var arr = new[] { o, o };
TestRoundTripWithoutReferencePreservation(arr);
private static void TestRoundTrip(Test[] arr)
var settings = new JsonSerializerSettings
Converters = { new ArrayReferencePreservngConverter() },
PreserveReferencesHandling = PreserveReferencesHandling.All
var json1 = JsonConvert.SerializeObject(arr, Formatting.Indented, settings);
Console.WriteLine("Initially serialized JSON: ");
Console.WriteLine(json1);
var a2 = JsonConvert.DeserializeObject<Test[]>(json1, settings);
var json2 = JsonConvert.SerializeObject(a2, Formatting.Indented, settings);
Console.WriteLine("Deserialized and re-serialized JSON: ");
Console.WriteLine(json2);
if (!JToken.DeepEquals(JToken.Parse(json1), JToken.Parse(json2)))
Console.WriteLine("Initial and re-serialized JSON are NOT equivalent.");
throw new InvalidOperationException("!JToken.DeepEquals(JToken.Parse(json1), JToken.Parse(json2))");
Console.WriteLine("Initial and re-serialized JSON are equivalent.");
private static void TestRoundTripWithoutReferencePreservation(Test[] arr)
var settings = new JsonSerializerSettings
Converters = { new ArrayReferencePreservngConverter() },
PreserveReferencesHandling = PreserveReferencesHandling.All
var jsonNoRef1 = JsonConvert.SerializeObject(arr, Formatting.Indented);
var arrNoRef = JsonConvert.DeserializeObject<Test[]>(jsonNoRef1, settings);
var jsonNoRef2 = JsonConvert.SerializeObject(arrNoRef, Formatting.Indented);
if (!JToken.DeepEquals(JToken.Parse(jsonNoRef1), JToken.Parse(jsonNoRef2)))
Console.WriteLine("Initial and re-serialized JSON without refs are NOT equivalent.");
throw new InvalidOperationException("!JToken.DeepEquals(JToken.Parse(jsonNoRef1), JToken.Parse(jsonNoRef2))");
Console.WriteLine("Initial and re-serialized JSON without refs are equivalent.");
public static void Main()
Console.WriteLine("Json.NET version: " + typeof(JsonSerializer).Assembly.FullName);