using System.Collections.Generic;
using System.Collections;
using System.Runtime.Serialization;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Converters;
public class ListFromDataContractXmlConverter : JsonConverter
readonly IContractResolver resolver;
public ListFromDataContractXmlConverter() : this(null) { }
public ListFromDataContractXmlConverter(IContractResolver resolver)
this.resolver = resolver ?? new JsonSerializer().ContractResolver;
static bool CanConvert(Type objectType, IContractResolver resolver)
JsonArrayContract contract;
return CanConvert(objectType, resolver, out itemType, out contract);
static bool CanConvert(Type objectType, IContractResolver resolver, out Type itemType, out JsonArrayContract contract)
if ((itemType = objectType.GetListItemType()) == null)
if ((contract = resolver.ResolveContract(objectType) as JsonArrayContract) == null)
var itemContract = resolver.ResolveContract(itemType);
if (itemContract is JsonArrayContract)
public override bool CanConvert(Type objectType) { return CanConvert(objectType, resolver); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
JsonArrayContract contract;
if (!CanConvert(objectType, serializer.ContractResolver, out itemType, out contract))
throw new JsonSerializationException(string.Format("Invalid type for {0}: {1}", GetType(), objectType));
var list = (IList)(existingValue ?? contract.DefaultCreator());
switch (reader.MoveToContentAndAssert().TokenType)
case JsonToken.StartArray:
serializer.Populate(reader, list);
case JsonToken.StartObject:
while (reader.ReadToContentAndAssert().TokenType != JsonToken.EndObject)
var name = (string)reader.AssertTokenType(JsonToken.PropertyName).Value;
reader.ReadToContentAndAssert();
if (name.StartsWith("@"))
if (!name.StartsWith("@xmlns:") && name.EndsWith("nil") && reader.TokenType == JsonToken.String && ((string)reader.Value) == "true")
if (reader.TokenType == JsonToken.StartArray)
serializer.Populate(reader, list);
list.Add(serializer.Deserialize(reader, itemType));
throw new JsonSerializationException(string.Format("Unexpected token {0}", reader.TokenType));
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
public static partial class JsonExtensions
public static JsonReader AssertTokenType(this JsonReader reader, JsonToken tokenType) =>
reader.TokenType == tokenType ? reader : throw new JsonSerializationException(string.Format("Unexpected token {0}, expected {1}", reader.TokenType, tokenType));
public static JsonReader ReadToContentAndAssert(this JsonReader reader) =>
reader.ReadAndAssert().MoveToContentAndAssert();
public static JsonReader MoveToContentAndAssert(this JsonReader reader)
throw new ArgumentNullException();
if (reader.TokenType == JsonToken.None)
while (reader.TokenType == JsonToken.Comment)
public static JsonReader ReadAndAssert(this JsonReader reader)
throw new ArgumentNullException();
throw new JsonReaderException("Unexpected end of JSON stream.");
public static Type GetListItemType(this Type type)
if (type.IsPrimitive || type.IsArray || type == typeof(string))
var genType = type.GetGenericTypeDefinition();
if (genType == typeof(List<>))
return type.GetGenericArguments()[0];
[DataContract(Name = "Person", Namespace = "http://schemas.datacontract.org/2004/07/Workflows.MassTransit.Hosting.Serialization")]
public sealed class Person
public string Name { get; set; }
public List<int> Numbers { get; set; }
[DataContract(Name = "Person", Namespace = "http://schemas.microsoft.com/2003/10/Serialization/Arrays")]
public sealed class Person2
public string Name { get; set; }
public List<int> Numbers { get; set; }
public static void Test()
Test(new Person { Name = "Test", Numbers = new List<int> { 1, 2, 3 } });
Test(new Person { Name = "Test", Numbers = new List<int> { 1 } });
Test(new Person { Name = "Test", Numbers = new List<int> { } });
Test(new Person { Name = "Test", Numbers = null });
Test(new Person2 { Name = "Test", Numbers = new List<int> { 1, 2, 3 } });
Test(new Person2 { Name = "Test", Numbers = new List<int> { 1 } });
Test(new Person2 { Name = "Test", Numbers = new List<int> { } });
Test(new Person2 { Name = "Test", Numbers = null });
Test(new List<int> { 1, 2, 3 });
Test(new List<int> { 1 });
Test(new List<string> { "A", "B", "C" });
Test(new List<string> { "A" });
Test(new List<string> { });
Test(new List<Person> { new Person { Name = "Test", Numbers = new List<int> { } }, new Person { Name = "Test", Numbers = new List<int> { } } });
Test(new List<Person> { new Person { Name = "Test", Numbers = new List<int> { } } });
Test(new List<Person> { });
Assert.Throws<JsonSerializationException>(() => Test(new List<List<int>> { new () { 1, 2, 3 } }));
static void Test<Person>(Person person)
var xmlString = DataContractSerializerHelper.ToContractXml(person);
Console.WriteLine("\nTesting {0} with XML:", person);
Console.WriteLine(xmlString);
var xelement = XElement.Parse(xmlString);
var jsonFromXml = JsonConvert.SerializeXNode(xelement, Formatting.Indented, omitRootObject: true);
Console.WriteLine("Intermediate JSON:");
Console.WriteLine(jsonFromXml);
var settings = new JsonSerializerSettings
Converters = { new ListFromDataContractXmlConverter() },
var deserializedPerson = JsonConvert.DeserializeObject<Person>(jsonFromXml, settings);
var personBackJson = JsonConvert.SerializeObject(deserializedPerson, Formatting.Indented);
Console.WriteLine("Resulting {0} re-serialized to JSON:", deserializedPerson);
Console.WriteLine(personBackJson);
Assert.AreEqual(JsonConvert.SerializeObject(person), JsonConvert.SerializeObject(deserializedPerson));
public static partial class DataContractSerializerHelper
public static string ToContractXml<T>(this T obj, DataContractSerializer serializer = null, System.Xml.XmlWriterSettings settings = null)
serializer = serializer ?? new DataContractSerializer(obj == null ? typeof(T) : obj.GetType());
using (var textWriter = new StringWriter())
settings = settings ?? new System.Xml.XmlWriterSettings { Indent = true };
using (var xmlWriter = System.Xml.XmlWriter.Create(textWriter, settings))
serializer.WriteObject(xmlWriter, obj);
return textWriter.ToString();
public static T FromContractXml<T>(string xml, DataContractSerializer serializer = null)
using (var textReader = new StringReader(xml ?? ""))
using (var xmlReader = System.Xml.XmlReader.Create(textReader))
return (T)(serializer ?? new DataContractSerializer(typeof(T))).ReadObject(xmlReader);
public static void Main()
Console.WriteLine("Environment version: {0} ({1})", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , GetNetCoreVersion());
Console.WriteLine("{0} version: {1}", typeof(JsonSerializer).Assembly.GetName().Name, typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");
public static string GetNetCoreVersion()
var assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly;
var assemblyPath = assembly.Location.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App");
if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2)
return assemblyPath[netCoreAppIndex + 1];