为Google Charts返回带有Date类型的JSON数组

本文关键字:类型 JSON 数组 Date Google Charts 返回 | 更新日期: 2023-09-27 18:09:57

我有一个来自Google Charts的' timeline '图表。当填充数据时,它需要一个JavaScript Date类型。

我的初始代码是这样的:

var container = document.getElementById('divChart1');
var chart = new google.visualization.Timeline(container);
var dataTable = new google.visualization.DataTable();
dataTable.addColumn({ type: 'string', id: 'Name' });
dataTable.addColumn({ type: 'string', id: 'Category' });
dataTable.addColumn({ type: 'date', id: 'Start' });
dataTable.addColumn({ type: 'date', id: 'End' });

现在我试图通过AJAX和日期类型填充它给我的问题。

需要像这样填充行:

dataTable.addRows([
  ['Aaa', 'A', new Date(2014, 1, 1), new Date(2016, 12, 31)],
  ['Bbb', 'B', new Date(2014, 1, 1), new Date(2016, 5, 31)]]);

是否有任何方法我可以返回一个序列化的集合从我的AJAX服务,只是解析它直接,或者我需要迭代集合和更新一个JavaScript日期每次?

当我尝试dataTable.addRows(JSON.parse(result.chartData));时,我得到以下错误:Error: Type mismatch. Value 2015-08-26T11:59:23.889004+02:00 does not match type date in column index 2

为了提供信息,下面是AJAX服务的样子:

List<List<object>> chartData = new List<List<object>>();
chartData.Add(new List<object>() { 
    "Aaa",
    "A",
    DateTime.Now,
    DateTime.Now.AddMonths(3)
});
return JsonConvert.SerializeObject(chartData);

edit:我已经让它工作了。仍在调整,但这是要点:

chartData.Add(new List<object>() { 
    "Aaa",
    "A",
    DateTime.Now.Year + "#" + DateTime.Now.Month + "#" + DateTime.Now.Day,
    DateTime.Now.AddMonths(3).Year + "#" + DateTime.Now.AddMonths(3).Month + "#" + DateTime.Now.AddMonths(3).Day
});
var result = $.parseJSON(result.chartData);
$.each(result, function (k, v) {
    var s = v[2].split('#');
    var e = v[3].split('#');
    dataTable.addRow([v[0], v[1], new Date(s[0], s[1], s[2]), new Date(e[0], e[1], e[2])]);
});

不打算用这个作为正式答案,因为它没有回答问题。

为Google Charts返回带有Date类型的JSON数组

更新

虽然JSON是根据您原来的问题所要求创建的,但似乎JSON.parse('new Date(2014, 1, 1)')不起作用,因为JavaScript日期构造器不是严格有效的JSON。

因此,您应该将DateTime序列化为字符串,并将一个恢复函数传递给JSON.parse(),该函数识别日期字符串并构建JavaScript Date。例如:
  • 如何解析JSON接收JavaScript中的日期对象?演示如何创建一个识别Microsoft格式日期的reviver函数。要以这种格式输出日期,请使用设置JsonSerializerSettings.DateFormatHandling = DateFormatHandling.MicrosoftDateFormat

  • 对于ISO日期格式的日期字符串,请参见这里的示例:JavaScript JSON日期解析和实际日期,或者这里:如何使用JSON。

原始回答

您可以扩展JavaScriptDateTimeConverternew Date(2014, 1, 1 [, H [, M [, S [, MS]]]])格式写入和读取日期(我正在扩展这个只读取不写入的答案):

public class JavaScriptYMDDateTimeConverter : JavaScriptDateTimeConverter
{
    public bool StripTimeOfDay { get; set; }
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (value is DateTime)
        {
            // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date
            // Note: Where Date is called as a constructor with more than one argument, the specifed arguments represent local time.
            var date = ((DateTime)value).ToLocalTime();
            writer.WriteStartConstructor("Date");
            writer.WriteValue(date.Year);
            writer.WriteValue(date.Month - 1);
            writer.WriteValue(date.Day);
            if (!StripTimeOfDay)
            {
                var written = date.Date;
                var epsilon = new TimeSpan(TimeSpan.TicksPerMillisecond);
                // Only write hours, min, sec, ms if needed.
                if (date < written - epsilon || date > written + epsilon)
                {
                    writer.WriteValue(date.Hour);
                    written = written.AddHours(date.Hour);
                }
                if (date < written - epsilon || date > written + epsilon)
                {
                    writer.WriteValue(date.Minute);
                    written = written.AddMinutes(date.Minute);
                }
                if (date < written - epsilon || date > written + epsilon)
                {
                    writer.WriteValue(date.Second);
                    written = written.AddSeconds(date.Second);
                }
                if (date < written - epsilon || date > written + epsilon)
                {
                    writer.WriteValue(date.Millisecond);
                    written = written.AddMilliseconds(date.Millisecond);
                }
            }
            writer.WriteEndConstructor();
        }
        else
        {
            // DateTimeOffset
            base.WriteJson(writer, value, serializer);
        }
    }
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        Type type = (Nullable.GetUnderlyingType(objectType) ?? objectType);
        bool isNullable = (Nullable.GetUnderlyingType(objectType) != null);
        var token = JToken.Load(reader);
        if (token == null || token.Type == JTokenType.Null)
        {
            if (!isNullable)
                throw new JsonSerializationException(string.Format("Null value for type {0} at path {1}", objectType.Name, reader.Path));
            return null;
        }
        if (token.Type != JTokenType.Constructor)
        {
            throw new JsonSerializationException(string.Format("Invalid Date constructor '"{0}'" at path {1}", token.ToString(), reader.Path));
        }
        var constructor = (JConstructor)token;
        if (!string.Equals(constructor.Name, "Date", StringComparison.Ordinal))
        {
            throw new JsonSerializationException(string.Format("Invalid Date constructor '"{0}'" at path {1}", token.ToString(), reader.Path));
        }
        var values = constructor.Values().ToArray();
        if (values.Length == 0)
        {
            throw new JsonSerializationException(string.Format("Invalid Date constructor '"{0}'" at path {1}", token.ToString(), reader.Path));
        }
        else if (values.Length == 1)
        {
            // Assume ticks
            using (var subReader = constructor.CreateReader())
            {
                while (subReader.TokenType != JsonToken.StartConstructor)
                    subReader.Read();
                return base.ReadJson(subReader, objectType, existingValue, serializer); // Use base class to convert
            }
        }
        else
        {
            var year = (values.Length > 0 ? (int)values[0] : 0);
            var month = (values.Length > 1 ? (int)values[1] : 0) + 1; // c# months go from 1 to 12, JavaScript from 0 to 11
            var day = (values.Length > 2 ? (int)values[2] : 0);
            var hour = (values.Length > 3 ? (int)values[3] : 0);
            var min = (values.Length > 4 ? (int)values[4] : 0);
            var sec = (values.Length > 5 ? (int)values[5] : 0);
            var ms = (values.Length > 6 ? (int)values[6] : 0);
            // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date
            // Note: Where Date is called as a constructor with more than one argument, the specifed arguments represent local time.
            var dt = new DateTime(year, month, day, hour, min, sec, ms, DateTimeKind.Local);
            if (type == typeof(DateTimeOffset))
                return new DateTimeOffset(dt);
            return dt;
        }
    }
}

然后像这样使用:

        var settings = new JsonSerializerSettings { Converters = new JsonConverter[] { new JavaScriptYMDDateTimeConverter { StripTimeOfDay = true } } }; 
        return JsonConvert.SerializeObject(chartData, settings);

输出
[["Aaa","A",new Date(2015,7,26),new Date(2015,10,26)]]

可以将日期放在括号中

{
      "cols": [
            {"label": "Date", "type": "date"},
            {"label": "Serie 1", "type": "number"},
            {"label": "Serie 2", "type": "number"},
      ],
      "rows": [
            {"c":[{"v": "Date(2014, 1, 1)"}, {"v": 1000}, {"v": 400}]},
            {"c":[{"v": "Date(2014, 2, 25)"}, {"v": 1170}, {"v": 460}]},
            {"c":[{"v": "Date(2014, 3, 15)"}, {"v": 660}, {"v": 1120}]},
            {"c":[{"v": "Date(2014, 4, 30)"}, {"v": 1030}, {"v": 540}]},
      ]
}

请注意Google关于Date构造函数和浏览器的建议:https://developers.google.com/chart/interactive/docs/datesandtimes