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 Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
public class StructuredListConverter<T> : JsonConverter
const string typeName = "type";
const string structureName = "structure";
const string listName = "list";
public override bool CanConvert(Type objectType)
if (typeof(ICollection<T>).IsAssignableFrom(objectType))
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
if (reader.TokenType == JsonToken.Null)
var collection = existingValue as ICollection<T> ?? (ICollection<T>) serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
var root = JObject.Load(reader);
var structure = root[structureName] == null ? null : root[structureName].ToObject<string []>();
throw new JsonSerializationException("structure not found.");
var listToken = root[listName];
if (listToken == null || listToken.Type == JTokenType.Null)
var list = listToken as JArray;
throw new JsonSerializationException("list was not an array.");
if (list == null || list.Count == 0)
foreach (var item in list)
if (item == null || item.Type == JTokenType.Null)
collection.Add(default(T));
else if (item.Type != JTokenType.Array)
throw new JsonSerializationException(string.Format("Item was not an array: {0}", item));
collection.Add(new JObject(item.Zip(structure, (i, n) => new JProperty(n, i))).ToObject<T>());
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
var contract = serializer.ContractResolver.ResolveContract(typeof(T)) as JsonObjectContract;
throw new JsonSerializationException(string.Format("Type {0} is not mapped to a JSON object.", typeof(T)));
var collection = (ICollection<T>)value;
writer.WriteStartObject();
writer.WritePropertyName(typeName);
serializer.Serialize(writer, typeof(T));
var structure = contract.Properties.Where(p => p.Readable && !p.Ignored).Select(p => p.PropertyName).ToList();
writer.WritePropertyName(structureName);
serializer.Serialize(writer, structure);
.Select(i => i == null ? null : contract.Properties.Where(p => p.Readable && !p.Ignored).Select(p => p.ValueProvider.GetValue(i)));
writer.WritePropertyName(listName);
serializer.Serialize(writer, query);
namespace Activity.Channel
public class LocationChannelEvent : Activity.Channel.Event
public double Latitude { get; set; }
public double Longitude { get; set; }
public float? Distance { get; set; }
public float? Altitude { get; set; }
public float? Speed { get; set; }
[JsonConverter(typeof(StructuredListConverter<LocationChannelEvent>))]
public List<LocationChannelEvent> events { get; set; }
public Location location { get; set; }
public static void Test()
var token = JToken.Parse(json);
Console.WriteLine("Original JSON: ");
Console.WriteLine(token);
var root = JsonConvert.DeserializeObject<RootObject>(json);
var json2 = JsonConvert.SerializeObject(root, Formatting.Indented);
Console.WriteLine("Deserialized and re-serialized JSON: ");
Console.WriteLine(json2);
""type"": ""Project.Model.ChannelEvents.LocationChannelEvent, Project, Version=1.2.7.0, Culture=neutral, PublicKeyToken=null"",
public static void Main()
Console.WriteLine("Environment version: " + Environment.Version);
Console.WriteLine("Json.NET version: " + typeof(JsonSerializer).Assembly.FullName);