Custom JavaScriptConverter for DateTime?
JavaScriptSerializer can definitely do what you desire.
It's possible to customize the serialization performed by JavaScriptSerializer for any type by creating a custom converter and registering it with the serializer. If you have a class called Person, we could create a converter like so:
public class Person{ public string Name { get; set; } public DateTime Birthday { get; set; }}public class PersonConverter : JavaScriptConverter{ private const string _dateFormat = "MM/dd/yyyy"; public override IEnumerable<Type> SupportedTypes { get { return new[] { typeof(Person) }; } } public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer) { Person p = new Person(); foreach (string key in dictionary.Keys) { switch (key) { case "Name": p.Name = (string)dictionary[key]; break; case "Birthday": p.Birthday = DateTime.ParseExact(dictionary[key] as string, _dateFormat, DateTimeFormatInfo.InvariantInfo); break; } } return p; } public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer) { Person p = (Person)obj; IDictionary<string, object> serialized = new Dictionary<string, object>(); serialized["Name"] = p.Name; serialized["Birthday"] = p.Birthday.ToString(_dateFormat); return serialized; }}
And use it like this:
JavaScriptSerializer serializer = new JavaScriptSerializer();serializer.RegisterConverters(new[] { new PersonConverter() });Person p = new Person { Name = "User Name", Birthday = DateTime.Now };string json = serializer.Serialize(p);Console.WriteLine(json);// {"Name":"User Name","Birthday":"12/20/2010"}Person fromJson = serializer.Deserialize<Person>(json);Console.WriteLine(String.Format("{0}, {1}", fromJson.Name, fromJson.Birthday)); // User Name, 12/20/2010 12:00:00 AM
Here's an enhancement for the accepted answer.
Using generics, passing a type and using reflection to determine the datetime properties.
public class ExtendedJavaScriptConverter<T> : JavaScriptConverter where T : new(){ private const string _dateFormat = "dd/MM/yyyy"; public override IEnumerable<Type> SupportedTypes { get { return new[] { typeof(T) }; } } public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer) { T p = new T(); var props = typeof(T).GetProperties(); foreach (string key in dictionary.Keys) { var prop = props.Where(t => t.Name == key).FirstOrDefault(); if (prop != null) { if (prop.PropertyType == typeof(DateTime)) { prop.SetValue(p, DateTime.ParseExact(dictionary[key] as string, _dateFormat, DateTimeFormatInfo.InvariantInfo), null); } else { prop.SetValue(p, dictionary[key], null); } } } return p; } public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer) { T p = (T)obj; IDictionary<string, object> serialized = new Dictionary<string, object>(); foreach (PropertyInfo pi in typeof(T).GetProperties()) { if (pi.PropertyType == typeof(DateTime)) { serialized[pi.Name] = ((DateTime)pi.GetValue(p, null)).ToString(_dateFormat); } else { serialized[pi.Name] = pi.GetValue(p, null); } } return serialized; } public static JavaScriptSerializer GetSerializer() { JavaScriptSerializer serializer = new JavaScriptSerializer(); serializer.RegisterConverters(new[] { new ExtendedJavaScriptConverter<T>() }); return serializer; }}
Usage is simple:
JavaScriptSerializer serialiser = ExtendedJavaScriptConverter<Task>.GetSerializer();
Hope that helps someone.
There is actually a nice clean way to do this without knowing the wrapper type or even needing a wrapper object.
You use JavaScriptConverter to convert your object to a Uri that also implements IDictionary. JavaScriptSerializer will serialize this as a string.
This hack is described here: Custom DateTime JSON Format for .NET JavaScriptSerializer