【问题标题】:Returning JSON arrays with Date types for Google Charts为 Google 图表返回具有日期类型的 JSON 数组
【发布时间】:2015-11-20 07:48:26
【问题描述】:

我有一个来自 Google Charts 的“时间线”图表。填充数据时需要 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 填充它,但 Date 类型给我带来了问题。

需要像这样填充行:

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);

编辑:好吧,我已经开始工作了。仍在调整,但这是要点:

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])]);
});

不打算将其用作官方答案,因为它没有回答问题。

【问题讨论】:

  • 你试过JSON日期格式吗?在 javascript 中,您可以通过 (new Date()).toJSON(); 获得它
  • 当您new Date(2014, 1, 1) 时,您不是在剥离一天中的时间信息吗?您的测试用例使用 DateTime.Now,其中包括一天中的时间。
  • @dbc,我只需要年/月/日。我以DateTime.Now 为例。我已尝试在服务器端格式化日期,但仍然存在问题。我想我本可以更清楚地说明这个问题,但我仍在考虑这个问题,更不用说试图解决它了

标签: javascript c# asp.net ajax json


【解决方案1】:

更新

虽然 JSON 是按照原始问题中的要求创建的,但似乎 JSON.parse('new Date(2014, 1, 1)') 不起作用,因为 JavaScript 日期构造函数不是 strictly valid JSON

因此,您应该将DateTime 序列化为字符串,并将reviver function 传递给JSON.parse(),该JSON.parse() 可以识别日期字符串并构造JavaScript Date。例如:

原答案

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

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)]]

【讨论】:

  • 好吧。这一切都是为了一个陌生人。谢谢大佬,试试这个如果我最终以不同的方式做这件事,我希望这不是坏业力:)
  • 我在尝试dataTable.addRows(JSON.parse(result.chartData)); 时收到SyntaxError: JSON.parse: unexpected keyword at line 1 column 21 of the JSON data,在尝试dataTable.addRows(result.chartData); 时收到Error: Argument given to addRows must be either a number or an array Source。不过这看起来很有希望
  • @user982119 - 我是 c# 程序员而不是 JavaScript 程序员,但似乎 JSON.parse() 不支持 new Date(2015,10,26) 语法,可能是因为它是 not valid JSON
【解决方案2】:

你可以把日期放在括号里

例如

{
      "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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-03-06
    • 2015-02-25
    • 1970-01-01
    • 1970-01-01
    • 2021-02-14
    • 2013-01-23
    • 1970-01-01
    相关资源
    最近更新 更多