using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.Runtime.Serialization;
using System.ComponentModel;
using System.Globalization;
using System.Collections.Specialized;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Runtime.Serialization.Formatters;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
public static void Test()
string inputJson = GetInitialJson();
Console.WriteLine("Initial JSON: ");
Console.WriteLine(inputJson);
using (var inputStream = new MemoryStream(Encoding.UTF8.GetBytes(inputJson)))
using (var outputStream = new MemoryStream())
Func<string, string> typeNameMapper = (t) =>
if (!t.EndsWith(", Version=1.2.7.0, Culture=neutral, PublicKeyToken=null"))
t = t + ", Version=1.2.7.0, Culture=neutral, PublicKeyToken=null";
"channels.heart-rate.events",
"channels.location.events"
JsonExtensions.ReformatCollections(inputStream, outputStream, paths, typeNameMapper, Formatting.Indented);
outputStream.Position = 0;
outputJson = new StreamReader(outputStream).ReadToEnd();
Console.WriteLine("Output JSON: ");
Console.WriteLine(outputJson);
var expectedJson = GetExpectedJson();
Assert.IsTrue(JToken.DeepEquals(JToken.Parse(outputJson), JToken.Parse(expectedJson)));
Console.WriteLine("Output JSON and expected JSON are equivalent.");
static string GetInitialJson()
var json = @"{""channels"": {
""$type"": ""System.Collections.Generic.List`1[[Project.Model.Activity+Channel+Event, Project]], mscorlib"",
""$type"": ""Project.Model.ChannelEvents.HeartRateChannelEvent, Project"",
""Date"": ""2017-03-31T00:00:00.00-04:00"",
""$type"": ""Project.Model.ChannelEvents.HeartRateChannelEvent, Project"",
""Date"": ""2017-03-31T00:00:00-04:00"",
""$type"": ""System.Collections.Generic.List`1[[Project.Model.Activity+Channel+Event, Project]], mscorlib"",
""$type"": ""Project.Model.ChannelEvents.LocationChannelEvent, Project"",
static string GetExpectedJson()
var json = @"{""channels"": {
""type"": ""Project.Model.ChannelEvents.HeartRateChannelEvent, Project, Version=1.2.7.0, Culture=neutral, PublicKeyToken=null"",
""2017-03-31T00:00:00.00-04:00""
""2017-03-31T00:00:00-04:00""
""type"": ""Project.Model.ChannelEvents.LocationChannelEvent, Project, Version=1.2.7.0, Culture=neutral, PublicKeyToken=null"",
public class AssertionFailedException : System.Exception
public AssertionFailedException() : base() { }
public AssertionFailedException(string s) : base(s) { }
public static class Assert
public static void IsTrue(bool value)
throw new AssertionFailedException("failed");
public static class JsonExtensions
const string JsonTypeName = @"$type";
const string JsonValuesName = @"$values";
public static void ReformatCollections(Stream inputStream, Stream outputStream, IEnumerable<string> paths, Func<string, string> typeNameMapper, Formatting formatting)
var root = JToken.Load(new JsonTextReader(new StreamReader(inputStream)) { DateParseHandling = DateParseHandling.None });
root = ReformatCollections(root, paths, typeNameMapper);
var writer = new StreamWriter(outputStream);
var jsonWriter = new JsonTextWriter(writer) { Formatting = formatting };
root.WriteTo(jsonWriter);
public static JToken ReformatCollections(JToken root, IEnumerable<string> paths, Func<string, string> typeNameMapper)
foreach (var path in paths)
var token = root.SelectToken(path);
var newToken = token.ReformatCollection(typeNameMapper);
public static JToken ReformatCollection(this JToken value, Func<string, string> typeNameMapper)
if (value == null || value.Type == JTokenType.Null)
var array = value as JArray;
array = value[JsonValuesName] as JArray;
var properties = new Dictionary<string, int>();
foreach (var item in array)
if (item.Type == JTokenType.Null)
var obj = item as JObject;
throw new JsonSerializationException(string.Format("Item \"{0}\" was not a JObject", obj.ToString(Formatting.None)));
var objType = (string)obj[JsonTypeName];
if (objType != null && type == null)
else if (objType != null && type != null)
throw new JsonSerializationException("Too many item types.");
foreach (var property in obj.Properties().Where(p => p.Name != JsonTypeName))
if (!properties.ContainsKey(property.Name))
properties.Add(property.Name, properties.Count);
var propertyList = properties.OrderBy(p => p.Value).Select(p => p.Key).ToArray();
var newValue = new JObject();
newValue["type"] = JToken.FromObject(typeNameMapper(type));
newValue["structure"] = JToken.FromObject(propertyList);
newValue["list"] = JToken.FromObject(array
.Select(o => (o.Type == JTokenType.Null ? o : propertyList.Where(p => o[p] != null).Select(p => o[p]))));
if (value.Parent != null)
public static void Main()
Console.WriteLine("Environment version: " + Environment.Version);
Console.WriteLine("Json.NET version: " + typeof(JsonSerializer).Assembly.FullName);