DateTime 的自定义 JavaScriptConverter?
Posted
技术标签:
【中文标题】DateTime 的自定义 JavaScriptConverter?【英文标题】:Custom JavaScriptConverter for DateTime? 【发布时间】:2010-11-23 10:12:55 【问题描述】:我有一个对象,它有一个 DateTime 属性...我想通过 AJAX/JSON 将该对象从 .ashx 处理程序传递回网页...我不想使用第 3 方控件... .
当我这样做时:
new javascriptSerializer().Serialize(DateTime.Now);
我明白了:
"\/Date(1251385232334)\/"
但我想要“2009 年 8 月 26 日”(没关系本地化......我的应用程序非常本地化,所以我的日期格式假设在这个问题中不存在争议)。如果我制作/注册自定义转换器
public class DateTimeConverter : JavaScriptConverter
public override IEnumerable<Type> SupportedTypes
get return new List<Type>() typeof(DateTime), typeof(DateTime?) ;
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
Dictionary<string, object> result = new Dictionary<string, object>();
if (obj == null) return result;
result["DateTime"] = ((DateTime)obj).ToShortDateString();
return result;
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
if (dictionary.ContainsKey("DateTime"))
return new DateTime(long.Parse(dictionary["DateTime"].ToString()), DateTimeKind.Unspecified);
return null;
然后我得到这个结果(因为自定义序列化方法的返回值是一个字典):
"DateTime":"8/27/2009"
所以现在在我的 Javascript 中,而不是这样做
somePerson.Birthday
我必须这样做
somePerson.Birthday.DateTime
or
somePerson.Birthday["DateTime"]
我怎样才能让自定义转换器返回一个直接字符串,以便我可以拥有干净的 Javascript?
【问题讨论】:
差不多 5 年后......我永远不会在 JSON 中使用“2009 年 8 月 27 日”的日期格式......我总是使用 ISO8601:2009-08-27T00:00:00Z 【参考方案1】:JavaScriptSerializer 绝对可以为所欲为。
可以通过创建自定义转换器并将其注册到序列化程序来自定义 JavaScriptSerializer 为任何类型执行的序列化。如果你有一个名为 Person 的类,我们可以像这样创建一个转换器:
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;
并像这样使用它:
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
【讨论】:
嘿看...一个老问题和老答案。我已经更新接受你的了【参考方案2】:这是对已接受答案的增强。
使用泛型、传递类型并使用反射来确定日期时间属性。
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;
用法很简单:
JavaScriptSerializer serialiser = ExtendedJavaScriptConverter<Task>.GetSerializer();
希望对某人有所帮助。
【讨论】:
不错。谢谢你。 这在我的类型有一个列表的情况下不起作用,即匿名表:List实际上有一个很好的干净方法可以做到这一点,而无需知道包装器类型,甚至不需要包装器对象。
您使用 JavaScriptConverter 将您的对象转换为同样实现 IDictionary 的 Uri。 JavaScriptSerializer 会将其序列化为字符串。
这里描述了这个 hack:Custom DateTime JSON Format for .NET JavaScriptSerializer
【讨论】:
这就是我需要将 DateTime 对象转换为匿名对象的属性,而无需将 DateTime 对象转换为具有日期时间的对象。 TL;博士;做它应该做的。【参考方案4】:其实有一个丑陋的方法,为容器(Person/Article/Whatever)类创建一个JavaScriptConverter
容器:
public class Article
public int Id get; set;
public string Title get; set;
public DateTime Date get; set;
转换器:
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"));
从此幸福...
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);
【讨论】:
【参考方案5】:我意识到这有点晚了,但我最近找到了一个非常好的解决这个问题的方法。它记录在这篇博文中,以防万一其他人发现它有用:http://icanmakethiswork.blogspot.co.uk/2012/04/beg-steal-or-borrow-decent-javascript.html
【讨论】:
【参考方案6】:答案是:你不能以这种方式使用 JavaScriptConverter...它没有这些功能。
但仅供参考:
How do I format a Microsoft JSON date? http://blog.stevenlevithan.com/archives/date-time-format
如果你在乎的话,我最终做的是向 javascript 字符串原型添加一个方法,以便我在代码中更轻松地做到这一点:
String.prototype.dateFromJSON = function ()
return eval(this.replace(/\/Date\((\d+)\)\//gi, "new Date($1)"));
;
在代码中使用这仍然很痛苦,因为您必须不断地在整个地方调用 dateFromJSON() ......这很愚蠢。
【讨论】:
【参考方案7】:我知道这看起来很愚蠢,但到目前为止我还没有找到更好的东西......不过我还在寻找,所以欢迎 cmets。
new JavaScriptSerializer().Serialize(DateTime.Now).Replace("\"\\/", "").Replace("\\/\"", "");
这只是删除了引号和斜杠,因此输出只是Date(123456789)
,虽然技术上不是文字,但浏览器将其理解为实际的日期值而不是字符串。
在 JSON 中,它看起来像这样
"myDate":Date(123456789)
我想是个黑客。如果这实际上是在生产代码中实现的,我会亲自将其包装起来,或者使用 FormatForDates() 之类的扩展方法,或者将序列化程序本身包装为装饰器模式……或者在这种情况下,使用“取消装饰器”。我一定真的想念这艘船,为什么这看起来这么难。我只想渲染一个日期,人! :-p
【讨论】:
我使用正则表达式得到了类似的结果:new Regex(@"""DateTime"":(""[0-9/-]+"")").Replace(s, "$1")
。结果看起来像"myDate":"5/3/2011"
。【参考方案8】:
@sambomartin 对答案的 vb.net 转换。这一切都归功于他。我只是把它贴在这里,以防有人需要这个用于 vb.net。
我还让它递归并添加了使用 XmlElement 数据注释覆盖默认属性名称的功能。 (XmlElementAttribute)
Imports System.Web.Script.Serialization
Imports System.Linq
Imports System.Globalization
Imports System.Xml.Serialization
Public Class ExtendedJavaScriptSerializer(Of T As New)
Inherits JavaScriptConverter
Private Const _dateFormat As String = "dd/MM/yyyy"
Public Overrides Function Deserialize(dictionary As IDictionary(Of String, Object), type As Type, serializer As JavaScriptSerializer) As Object
Dim p As New T()
Dim props = GetType(T).GetProperties()
For Each key As String In dictionary.Keys
Dim prop = props.Where(Function(x) x.Name = key).FirstOrDefault()
If prop IsNot Nothing Then
If prop.PropertyType = GetType(DateTime) Then
prop.SetValue(p, DateTime.ParseExact(CStr(dictionary(key)), _dateFormat, DateTimeFormatInfo.InvariantInfo), Nothing)
Else
prop.SetValue(p, dictionary(key), Nothing)
End If
End If
Next
Return p
End Function
Public Overrides Function Serialize(obj As Object, serializer As JavaScriptSerializer) As IDictionary(Of String, Object)
Dim serialized As IDictionary(Of String, Object) = New Dictionary(Of String, Object)
CheckProperties(obj, serialized)
Return serialized
End Function
Public Overrides ReadOnly Property SupportedTypes As IEnumerable(Of Type)
Get
Return GetType(T)
End Get
End Property
Private Sub CheckProperties(obj As Object, ByRef serialized As IDictionary(Of String, Object))
If obj Is Nothing Then Return
Dim objType As Type = obj.GetType()
For Each pi In objType.GetProperties()
' define serialization attribute name from '
' xmlelement dataannotation'
Dim displayname As String = pi.Name
Dim attrs() As Object = pi.GetCustomAttributes(True)
For Each attr In attrs
If GetType(XmlElementAttribute) = attr.GetType() Then
displayname = CType(attr, XmlElementAttribute).ElementName
End If
Next
' fix date format'
If pi.PropertyType = GetType(DateTime) Then
serialized(displayname) = CType(pi.GetValue(obj, Nothing), DateTime).ToString(_dateFormat)
Else
' recursive'
If pi.PropertyType.Assembly = objType.Assembly Then
CheckProperties(pi.GetValue(obj, Nothing), serialized)
Else
If pi.GetValue(obj, Nothing) IsNot Nothing Then
serialized(displayname) = pi.GetValue(obj, Nothing)
End If
End If
End If
Next
End Sub
Public Shared Function GetSerializer() As JavaScriptSerializer
Dim serializer As New JavaScriptSerializer
serializer.RegisterConverters(New ExtendedJavaScriptSerializer(Of T))
Return serializer
End Function
End Class
【讨论】:
【参考方案9】:我有一个类似的问题,我希望类 SensorReading 具有枚举属性“类型”和“单位”,以使用枚举值的名称进行序列化。 (如果 Enum 没有明确的数值,则默认结果为 0)
之前的序列化结果是这样的:
["id":"0","type":0,"value":"44.00","unit":0]
我想要的是这样的:
["id":"0","type":"temperature","value":"44.00","unit":"C"]
在下面的示例函数中,我在序列化对象之前注册了一个自定义序列化程序“EnumConverterSensorReading>”。
public static string ToSJSon(object obj)
var jss = new JavaScriptSerializer();
jss.RegisterConverters(new[] new EnumConverter<SensorReading>() );
return jss.Serialize(obj);
这是通用的 EnumConverter
public class EnumConverter<T> : JavaScriptConverter
public override IEnumerable<Type> SupportedTypes
get
return new[] typeof(T) ;
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
throw new NotImplementedException(String.Format("'0' does not yet implement 'Deserialize", this.GetType().Name));
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
IDictionary<string, object> serialized = new Dictionary<string, object>();
if (obj.GetType() == typeof(T))
if (obj.GetType().IsEnum)
serialized[obj.GetType().Name] = Enum.GetName(obj.GetType(), obj); ;
else
var sourceType = obj.GetType();
var properties = sourceType.GetProperties();
foreach (PropertyInfo property in properties)
if (property.CanRead)
if (property.PropertyType.IsEnum)
var str = Enum.GetName(property.PropertyType, property.GetValue(obj, null));
serialized[property.Name] = str;
else
serialized[property.Name] = property.GetValue(obj, null);
return serialized;
自定义序列化器宣布它序列化 T 类型的对象,并在 Serialize 中循环所有可读属性。如果属性是一个枚举,它会返回名称而不是值给字典。
这可以扩展到其他不按我们想要的方式序列化的属性类型。
如果自定义序列化程序 T 恰好是 Enum,我添加了一个单独的测试。然后改为输出 Enum 类的名称及其值。结果将如下所示:
["id":"0","type":"ReadingType":"temperature","value":"44.00","unit":"ReadingUnit":"C"]
如果您打算反序列化并想知道值属于哪种 Enum 类型,这可能是一个更好的输出。
【讨论】:
【参考方案10】:link text 这个例子有效
JavaScriptSerializer serializer = new JavaScriptSerializer();
DateTime dt = DateTime.Now;
DateTime dt1 = dt;
string jsonDateNow = serializer.Serialize(dt1);
【讨论】:
该示例绝对行不通...它与我在帖子顶部列出的完全相同...所以它只输出“\/Date(1251467694063)\/”不是我想要的。但感谢 4 到另一个类似线程的链接 这个例子非常适合我,奇怪的是如果我没有创建变量“dt1”序列化错误。以上是关于DateTime 的自定义 JavaScriptConverter?的主要内容,如果未能解决你的问题,请参考以下文章
SQLite SELECT查询中的自定义字段填充后不会成为DataTable中的DateTime列?
Python json.dumps 特殊数据类型的自定义序列化操作
使用 Play Framework JSON 库的自定义 Joda 时间序列化器?