从JavaScriptSerializer迁移到System.Text.Json的日期格式兼容问题
解决System.Text.Json无法识别JavaScriptSerializer日期格式的问题
问题重现
给定如下测试代码,System.Text.Json在解析\/Date(1471463788903)\/格式的日期时会抛出异常,而JavaScriptSerializer和Newtonsoft.Json可以正常解析:
var str = "{ \"Date\": \"\\/Date(1471463788903)\\/\" }"; var js = new JavaScriptSerializer().Deserialize<D>(str); var newtonsoft = JsonConvert.DeserializeObject<D>(str); var system = JsonSerializer.Deserialize<D>(str); // 此处抛出异常 public class D { public DateTime Date { get; set; } }
解决办法
System.Text.Json默认不支持JavaScriptSerializer的旧日期格式,需要自定义JsonConverter来处理这种格式:
自定义日期转换器
public class JavaScriptDateTimeConverter : JsonConverter<DateTime> { public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType != JsonTokenType.String) throw new JsonException("日期字段必须为字符串格式"); var dateStr = reader.GetString(); // 跳过前后的\/Date(和)\/,提取时间戳部分 var start = dateStr.IndexOf('(') + 1; var end = dateStr.IndexOf(')'); if (start <= 0 || end <= start) throw new JsonException($"无效的日期格式:{dateStr}"); if (!long.TryParse(dateStr.Substring(start, end - start), out var timestampMs)) throw new JsonException($"无法解析时间戳:{dateStr}"); // 转换为DateTime:Unix时间戳(毫秒)转本地时间,和JavaScriptSerializer行为一致 return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) .AddMilliseconds(timestampMs) .ToLocalTime(); } public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) { // 如果需要序列化回旧格式,取消下面的注释;否则使用默认ISO格式 /* var utcValue = value.ToUniversalTime(); var timestampMs = (long)(utcValue - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds; writer.WriteStringValue($"\\/Date({timestampMs})\\/"); */ // 默认输出ISO 8601格式 writer.WriteStringValue(value); } }
使用转换器解析
在反序列化时,将自定义转换器添加到JsonSerializerOptions中:
var options = new JsonSerializerOptions(); options.Converters.Add(new JavaScriptDateTimeConverter()); var system = JsonSerializer.Deserialize<D>(str, options); // 正常解析
潜在问题
- 时区一致性:
JavaScriptSerializer默认将Unix时间戳转换为本地时间,而System.Text.Json默认处理UTC时间。自定义转换器中已对齐JavaScriptSerializer的行为,但如果你的业务依赖UTC时间,需要修改ToLocalTime()为ToUniversalTime()或直接保留UTC时间。 - 负时间戳支持:如果存在1970年1月1日之前的日期,时间戳会是负数,当前转换器已经支持(因为用了
long.TryParse),但如果之前用正则需要确保包含负号。 - 可空日期的处理:如果模型中存在
DateTime?类型,需要单独实现JsonConverter<DateTime?>,或者修改现有转换器兼容可空类型(判断typeToConvert是否为可空类型)。 - 性能优化:如果需要处理大量日期解析,字符串截取比正则表达式效率更高,当前转换器已使用字符串截取,避免了正则的性能开销。
- 序列化兼容性:如果需要将对象序列化回旧格式,必须实现
Write方法;否则默认会输出ISO 8601格式,可能导致旧系统无法识别。
内容的提问来源于stack exchange,提问作者Yola
相关产品推荐
相关产品推荐

