I have an object, it has a DateTime property... I want to pass that object from an .ashx handler back to a webpage via AJAX/JSON... I don\'t want to use 3rd party controls..
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.
Actually there is an ugly way, create a JavaScriptConverter for the container (Person/Article/Whatever) class
Container:
public class Article
{
public int Id { get; set; }
public string Title { get; set; }
public DateTime Date { get; set; }
}
Converter:
public class ArticleJavaScriptConverter : JavaScriptConverter
{
public override IEnumerable<Type> SupportedTypes
{
get { return new Type[] { typeof(Article) }; }
}
public override object Deserialize(
IDictionary<string, object> dictionary,
Type type, JavaScriptSerializer serializer)
{
DateTime date =
DateTime.ParseExact(dictionary["date"] as string, "s", null);
return
new Article()
{
Id = (int)dictionary["id"],
Title = dictionary["title"] as string,
Date = date
};
}
public override IDictionary<string, object> Serialize(
object obj, JavaScriptSerializer serializer)
{
var article = obj as Article;
var result = new Dictionary<string,object>();
if (article != null)
{
this.SerializeInternal(article, result);
}
return result;
}
private void SerializeInternal(
Article article, IDictionary<string, object> result)
{
result.Add("id", article.Id);
result.Add("title", article.Title);
result.Add("date", article.Date.ToString("s"));
}
}
Happily ever after...
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(
new JavaScriptConverter[] {
new ArticleJavaScriptConverter()
});
var expected = new Article()
{
Id = 3,
Title = "test",
Date = DateTime.Now
};
// {"id":3,"title":"test","date":"2009-12-02T05:12:00"}
var json = serializer.Serialize(article);
var actual = serializer.Deserialize<Article>(json);
Assert.AreEqual(expected, actual);
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
link text This example works
JavaScriptSerializer serializer = new JavaScriptSerializer();
DateTime dt = DateTime.Now;
DateTime dt1 = dt;
string jsonDateNow = serializer.Serialize(dt1);