using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.Xml.Serialization;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
public string Id { get; set; }
[XmlElement(IsNullable = true)]
public DateTime? RenewalDate { get; set; }
public Vehicle Vehicle { get; set; }
public class NullableStructConverter<T> : JsonConverter where T : struct
public override bool CanConvert(Type objectType)
return objectType == typeof(Nullable<T>);
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
var underlyingType = Nullable.GetUnderlyingType(objectType);
if (underlyingType == null)
throw new InvalidOperationException(string.Format("Type {0} is not nullable", objectType));
var token = JToken.Load(reader);
if (token.Type == JTokenType.Null)
if (token.WasNilXmlElement())
return token.ToObject(underlyingType, serializer);
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
throw new NotImplementedException();
public static partial class JTokenExtensions
public static bool WasNilXmlElement(this JToken token)
if (token.Type == JTokenType.Null)
var obj = token as JObject;
if (obj.Properties().All(p => p.Name.StartsWith("@"))
&& obj.Properties().Any(p => p.Name == "@nil" || p.Name.EndsWith(":nil") && p.Value.ToString() == "true"))
public static void Test()
Console.WriteLine("Testing deserialization of JSON: ");
var settings = new JsonSerializerSettings
Converters = { new NullableStructConverter<DateTime>() }
var root = JsonConvert.DeserializeObject<RootObject[]>(json, settings);
Assert.IsTrue(root.Length == 2
&& root[0].Vehicle.RenewalDate == null
&& root[1].Vehicle.RenewalDate == new DateTime(2017, 7, 16, 0, 0, 0, DateTimeKind.Utc));
Console.WriteLine("Deserialization and re-serialization successful, result: ");
Console.WriteLine(JsonConvert.SerializeObject(root, Newtonsoft.Json.Formatting.Indented, settings));
Test(new RootObject { Vehicle = new Vehicle { Id = "101", RenewalDate = null } }, settings);
Test(new RootObject { Vehicle = new Vehicle { Id = "101", RenewalDate = DateTime.UtcNow } }, settings);
Console.WriteLine("\nAll tests passed successfully.");
static void Test<T>(T obj, JsonSerializerSettings settings)
private static void Test<T>(string xml, JsonSerializerSettings settings)
Console.WriteLine(string.Format("Testing round-trip of type {0} with input XML as follows: ", typeof(T)));
var json = JsonConvert.SerializeXNode(XElement.Parse(xml), Newtonsoft.Json.Formatting.Indented, true);
Console.WriteLine("Intermediate JSON: ");
var obj2 = JsonConvert.DeserializeObject<T>(json, settings);
var xml2 = obj2.GetXml();
var ok = XNode.DeepEquals(XElement.Parse(xml), XElement.Parse(xml2));
Console.WriteLine("Input XML and reserialized XML are equivalent.");
""RenewalDate"": ""2017-07-16T00:00:00Z""
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 partial class XmlSerializerExtensions
public static string GetXml<T>(this T obj, bool omitStandardNamespaces = true)
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))
new XmlSerializer(obj.GetType()).Serialize(xmlWriter, obj, ns);
return textWriter.ToString();
public static void Main()
Console.WriteLine("Environment version: " + Environment.Version);
Console.WriteLine("Json.NET version: " + typeof(JsonSerializer).Assembly.FullName);