using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Xml.Serialization;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
[Newtonsoft.Json.JsonConverter(typeof(FooListResponseConverter))]
[Serializable, XmlType(AnonymousType = true), XmlRoot(Namespace = "", ElementName = "FooList", IsNullable = false)]
public class FooListResponse
[XmlElement("Foo", IsNullable = false)]
public List<Foo> FooList { get; set; }
[XmlElement("Name"), JsonProperty("Name")]
public string Name { get; set; }
[JsonConverter(typeof(ListAsListOfObjectsWithDuplicateNameConverter<string>),
new object [] { "Color" })]
public List<string> Colors { get; set; }
public class FooListResponseConverter : WrappedListAsListOfObjectsWithDuplicateNameConverter<FooListResponse, Foo>
public FooListResponseConverter() : base("Foo") { }
public class WrappedListAsListOfObjectsWithDuplicateNameConverter<TWrapper, TItem> : JsonConverter<TWrapper> where TWrapper : new()
public WrappedListAsListOfObjectsWithDuplicateNameConverter(string name)
throw new ArgumentNullException("name");
static void GetContractAndProperty(JsonSerializer serializer, Type objectType, out JsonObjectContract contract, out JsonProperty property)
var c = serializer.ContractResolver.ResolveContract(objectType);
if (!(c is JsonObjectContract))
throw new JsonSerializationException(string.Format("Contract for {0} was of wrong type {1}", objectType, c.GetType()));
contract = (JsonObjectContract)c;
property = contract.Properties.Where(p => !p.Ignored).Single();
if (!typeof(List<TItem>).IsAssignableFrom(property.PropertyType))
throw new JsonSerializationException(string.Format("Invalid type {0} for property {1}", property.PropertyType, property.UnderlyingName));
public override void WriteJson(JsonWriter writer, TWrapper value, JsonSerializer serializer)
JsonObjectContract contract;
GetContractAndProperty(serializer, typeof(TWrapper), out contract, out property);
JsonExtensions.WriteListAsListOfObjectsWithDuplicateName<TItem>(writer, (List<TItem>)property.ValueProvider.GetValue(value), serializer, name);
public override TWrapper ReadJson(JsonReader reader, Type objectType, TWrapper existingValue, bool hasExistingValue, JsonSerializer serializer)
JsonObjectContract contract;
GetContractAndProperty(serializer, typeof(TWrapper), out contract, out property);
var existingListValue = hasExistingValue && existingValue != null
? (List<TItem>)property.ValueProvider.GetValue(existingValue)
var list = JsonExtensions.ReadListAsListOfObjectsWithDuplicateName<TItem>(reader, property.PropertyType, existingListValue, serializer, name);
var value = hasExistingValue && existingValue != null ? existingValue : (TWrapper)contract.DefaultCreator();
property.ValueProvider.SetValue(value, list);
public class ListAsListOfObjectsWithDuplicateNameConverter<T> : JsonConverter<List<T>>
public ListAsListOfObjectsWithDuplicateNameConverter(string name)
throw new ArgumentNullException("name");
public override void WriteJson(JsonWriter writer, List<T> value, JsonSerializer serializer)
JsonExtensions.WriteListAsListOfObjectsWithDuplicateName<T>(writer, value, serializer, name);
public override List<T> ReadJson(JsonReader reader, Type objectType, List<T> existingValue, bool hasExistingValue, JsonSerializer serializer)
return JsonExtensions.ReadListAsListOfObjectsWithDuplicateName<T>(reader, objectType, existingValue, serializer, name);
public class ListAsObjectWithDuplicateNameConverter<T> : JsonConverter<List<T>>
public ListAsObjectWithDuplicateNameConverter(string name)
throw new ArgumentNullException("name");
public override void WriteJson(JsonWriter writer, List<T> value, JsonSerializer serializer)
JsonExtensions.WriteListAsObjectWithDuplicateName<T>(writer, value, serializer, name);
public override List<T> ReadJson(JsonReader reader, Type objectType, List<T> existingValue, bool hasExistingValue, JsonSerializer serializer)
return JsonExtensions.ReadListAsObjectWithDuplicateName<T>(reader, objectType, existingValue, serializer, name);
public static partial class JsonExtensions
static List<T> CreateList<T>(Type objectType, List<T> existingValue, JsonSerializer serializer)
return existingValue ?? (objectType == typeof(List<T>)
: (List<T>)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator());
public static void WriteListAsListOfObjectsWithDuplicateName<T>(JsonWriter writer, List<T> value, JsonSerializer serializer, string name)
writer.WriteStartArray();
WriteListAsObjectWithDuplicateName<T>(writer, value, serializer, name);
public static List<T> ReadListAsListOfObjectsWithDuplicateName<T>(JsonReader reader, Type objectType, List<T> existingValue, JsonSerializer serializer, string name)
if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
else if (reader.TokenType != JsonToken.StartArray)
throw new JsonSerializationException(string.Format("Unexpected token {0}", reader.TokenType));
var list = CreateList(objectType, existingValue, serializer);
while (reader.ReadToContentAndAssert().TokenType != JsonToken.EndArray)
ReadListAsObjectWithDuplicateName<T>(reader, objectType, list, serializer, name);
public static void WriteListAsObjectWithDuplicateName<T>(JsonWriter writer, List<T> value, JsonSerializer serializer, string name)
writer.WriteStartObject();
foreach (var foo in value)
writer.WritePropertyName(name);
serializer.Serialize(writer, foo);
public static List<T> ReadListAsObjectWithDuplicateName<T>(JsonReader reader, Type objectType, List<T> existingValue, JsonSerializer serializer, string name)
if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
else if (reader.TokenType != JsonToken.StartObject)
throw new JsonSerializationException(string.Format("Unexpected token {0}", reader.TokenType));
var list = CreateList(objectType, existingValue, serializer);
while (reader.ReadToContentAndAssert().TokenType != JsonToken.EndObject)
if (reader.TokenType != JsonToken.PropertyName)
throw new JsonSerializationException(string.Format("Unexpected token {0}", reader.TokenType));
var propertyName = (string)reader.Value;
if (!string.Equals(name, propertyName, StringComparison.OrdinalIgnoreCase))
throw new JsonSerializationException(string.Format("Unexpected property name {0}", propertyName));
list.Add(serializer.Deserialize<T>(reader));
public static JsonReader ReadToContentAndAssert(this JsonReader reader)
return reader.ReadAndAssert().MoveToContentAndAssert();
public static JsonReader MoveToContentAndAssert(this JsonReader reader)
throw new ArgumentNullException("reader");
if (reader.TokenType == JsonToken.None)
while (reader.TokenType == JsonToken.Comment)
public static JsonReader ReadAndAssert(this JsonReader reader)
throw new ArgumentNullException("reader");
throw new JsonReaderException("Unexpected end of JSON stream.");
public static void Test()
Console.WriteLine(GetRequiredJson());
var fooListResponse = inputXml.LoadFromXml<FooListResponse>();
var json = JsonConvert.SerializeObject(fooListResponse, Newtonsoft.Json.Formatting.Indented);
var fooListResponse2 = JsonConvert.DeserializeObject<FooListResponse>(json);
var json2 = JsonConvert.SerializeObject(fooListResponse2, Newtonsoft.Json.Formatting.Indented);
Console.WriteLine("Re-serialized {0}:", fooListResponse);
Assert.Equal(json, json2);
Assert.Equal(fooListResponse.FooList.Count, 2);
Assert.True(json2.ReplaceWhitespace() == GetRequiredJson().ReplaceWhitespace());
public static void TestXml()
var fooListResponse = inputXml.LoadFromXml<FooListResponse>();
var xml = fooListResponse.GetXml(omitStandardNamespaces : true);
Console.WriteLine("Re-serialized {0}:", fooListResponse);
Assert.Equal(XNode.DeepEquals(XElement.Parse(xml), XElement.Parse(inputXml)), true);
Assert.Equal(fooListResponse.FooList.Count, 2);
foreach (var json in tests)
var fooListResponse = JsonConvert.DeserializeObject<FooListResponse>(json);
Assert.Equal(0, fooListResponse.FooList.Count);
Console.WriteLine(JsonConvert.SerializeObject(fooListResponse));
return @"<?xml version=""1.0"" encoding=""utf-8""?>
static string GetRequiredJson()
""Colors"": [ { ""Color"": ""Blue"", ""Color"": ""Yellow"" } ]
""Colors"": [ { ""Color"": ""Green"" } ]
public static class StringExtensions
private static readonly Regex sWhitespace = new Regex(@"\s+");
public static string ReplaceWhitespace(this string input, string replacement = "")
return sWhitespace.Replace(input, replacement);
public static class XmlSerializationHelper
public static T LoadFromXml<T>(this string xmlString, XmlSerializer serial = null)
serial = serial ?? new XmlSerializer(typeof(T));
using (var reader = new StringReader(xmlString))
return (T)serial.Deserialize(reader);
public static string GetXml<T>(this T obj, XmlSerializer serializer = null, bool omitStandardNamespaces = false)
XmlSerializerNamespaces ns = null;
if (omitStandardNamespaces)
ns = new XmlSerializerNamespaces();
using (var textWriter = new StringWriter())
var settings = new XmlWriterSettings() { Indent = true };
using (var xmlWriter = XmlWriter.Create(textWriter, settings))
(serializer ?? new XmlSerializer(obj.GetType())).Serialize(xmlWriter, obj, ns);
return textWriter.ToString();
public static void Main()
Console.WriteLine("Environment version: " + Environment.Version);
Console.WriteLine("{0} version: {1}", typeof(JsonSerializer).Namespace, typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");