18

我正在尝试反序列化一个包含 javascript 日期的 json 对象。在对象上调用 JSON.stringify 时,日期被序列化为未正确反序列化回日期的字符串。我尝试使用带有 chrome、IE 和 FF 的本机浏览器实现以及使用 jquery 来反序列化对象。两者都给出了一些结果。这是片段:

var obj = {Date: new Date()};
var objSer = JSON.stringify(obj);
var objDeser = JSON.parse(objSer);
var objJqDeser = $.parseJSON(objSer);

function getYear(value){
  try{
     return value.getYear();
  }
  catch(err){
    return err;
  }
}

$("#orig").text("Orig Year: " + getYear(obj.Date));
$("#deser").text("Deser Year: " + getYear(objDeser.Date));
$("#jqDeser").text("JqDeser Year: " + getYear(objJqDeser.Date));

我希望 objDeser.Date 成为 js 日期而不是字符串。你可以在这里看到这个问题:http: //jsbin.com/unijud/24/edit。是否有任何 js 库可以在构建 javascript 对象时正确反序列化日期?

4

6 回答 6

12

JSON.parse有一个鲜为人知的第二个参数:'reviver' 函数。这正是用于此目的:Date在初始解析期间将日期字符串恢复为对象(或者,假设是您想要从字符串转换的任何其他类型的对象)。

有一篇关于此的SO 帖子,这里有一篇博文,其中包括一个实现示例和一个函数,该函数将在解析为Date.

这是该博客文章中的关键功能,fwiw:

// JSON date deserializer
// use as the second, 'reviver' argument to JSON.parse();

if (window.JSON && !window.JSON.dateParser) {
    var reISO = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*))(?:Z|(\+|-)([\d|:]*))?$/;
    var reMsAjax = /^\/Date\((d|-|.*)\)[\/|\\]$/;

    JSON.dateParser = function (key, value) {
        // first, just make sure the property is a string:
        if (typeof value === 'string') {
            // then, use regex to see if it's an ISO-formatted string
            var a = reISO.exec(value);
            if (a) {
                // if so, Date() can parse it:
                return new Date(value);
            }
            // otherwise, see if it's a wacky Microsoft-format string:
            a = reMsAjax.exec(value);
            if (a) {
                // and perform some jujitsu to make use of it:
                var b = a[1].split(/[-+,.]/);
                return new Date(b[0] ? +b[0] : 0 - +b[1]);
            }
            // here, you could insert any additional tests and parse instructions you like, for other date syntaxes...
        }
        // important: you need to return any values you're not parsing, or they die...
        return value;
    };
}

// use: JSON.parse(json,JSON.dateParser); 

(关于 ISO 8601 日期的正确正则表达式有很多意见。YMMV。此外,没有特别的理由将函数打到全局 JSON 对象上。你可以在任何你喜欢的地方存储/引用它。)

于 2014-05-16T01:30:03.333 回答
5

我接受了@LastCoder 的建议并编写了一个简单的实现。它似乎正在做我想做的事。

var jsonDates = {
  dtrx2: /\d{4}-\d{2}-\d{2}/,
  parse: function(obj){
      var parsedObj = JSON.parse(obj);
      return this.parseDates(parsedObj);
  },
  parseDates: function(obj){
    // iterate properties
    for(pName in obj){

      // make sure the property is 'truthy'
      if (obj[pName]){
        var value = obj[pName];
        // determine if the property is an array
        if (Array.isArray(value)){
          for(var ii = 0; ii < value.length; ii++){
            this.parseDates(value[ii]);
          }
        }
        // determine if the property is an object
        else if (typeof(value) == "object"){
          this.parseDates(value);
        }
        // determine if the property is a string containing a date
        else if (typeof(value) == "string" && this.dtrx2.test(value)){
          // parse and replace
          obj[pName] = new Date(obj[pName]);
        }
      }
    }

    return obj;
  }
};

jsbin上提供了一个实时示例。gist上提供了参考。

于 2013-01-24T00:22:37.250 回答
5

为了使用 JavaScript 表示日期,我发现 JSON 使用 ISO 8601,一种将日期编码为字符串的特定字符串格式。不过,当我上次检查时,日期格式应该是什么样子并没有官方标准。主要浏览器使用 ISO 8601 作为 JSON 日期编码格式。

因此,日期被编码为 ISO 8601 字符串,然后在 JSON 被序列化和反序列化时就像常规字符串一样使用。

也就是说,可以使用 JavaScript Date 构造函数将 ISO 日期转换为 JavaScript 日期,该构造函数接受各种输入来构造日期,ISO 8601 就是其中之一。

获取今天的日期:

 var curDate = new Date();
document.write(curDate); //Mon Feb 01 2016 12:57:12 GMT-0600 (Central Standard Time)

将其解析为字符串:

var dateStr = JSON.parse(JSON.stringify(curDate));
document.write(dateStr);//2016-02-01T18:59:35.375Z

然后使用构造函数将其转换回 javascript 日期:

var date = new Date(dateStr);
document.write(date); //Mon Feb 01 2016 12:59:35 GMT-0600 (Central Standard Time)
于 2016-02-01T18:26:11.780 回答
3

JSON 规范不包括日期的特殊格式。因此,它们通常被序列化为字符串,有时带有特殊标记以表明如果语言支持它们,则应将其视为 Date 对象。因此,大多数(全部?)浏览器原生 JSON 解析器无法正确地往返 Date 对象。

有几个很好的库可以帮助解决这个问题——尽管我过去也使用过 datejs ,但我非常喜欢MomentJS。您只需要遍历您的对象并将正确的字段在解析后转换为 Date 对象。

我发现记住 JSON 格式比 JavaScript 对象字面量表示法更受限制是很有帮助的。

于 2013-01-23T20:43:20.743 回答
1

JavaScript es5 能够像2018-04-03T22:00:00...在其默认Date构造函数中一样解析日期。(例如new Date("2018-04-03T22:00:00...");.

对于像我这样正在从 JSON Web 响应中搜索自动日期反序列化的人来说,这可能很方便。

/**
 * Iterates over all entries of the input object and replace the string dates with the objects of {@link Date}.
*/
function fixDateObjects(responseBody) {
  if (responseBody) {
    const regex = /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}/;
    for (const [key, value] of Object.entries(responseBody)) {
      const val = String(value);
      if (val.startsWith('[object Object]')) {
        fixDateObjects(value);
      }
      if (val.match(regex)) {
        responseBody[key] = new Date(val);
      }
    }
  }
}

说明:它遍历 JSON(responseBody) 的所有对象条目,并用对象替换字符串日期(由给定的正则表达式匹配)new Date(str)

结果:这为您带来了进一步处理的自由。现在您已经完全反序列化了所有日期字符串。

//usage
function parseBody(response) {
   fixDateObjects(response);
   
   console.log(response.someDate); // Mon Aug 30 2021 22:45:59 GMT+0200 (...)
   // further processing
}
于 2021-08-30T20:52:22.443 回答
0

您可以手动将所需的所有日期函数添加到 String.prototype。

String.prototype.getYear = function() {
    return Date.parse(this).getYear();
};
var obj = {date: new Date()};
var dtObj = JSON.parse(JSON.stringify(obj));
console.log(dtObj.date.getYear());

或者您可以覆盖 JSON.parse 并让它遍历结果对象以查找与时间戳正则表达式匹配的字符串,然后将它们转换为 Date 对象。

var JSON_parse = JSON.parse;
JSON.parse = function(str) {
    var res = JSON_parse(str);
    findAndConvertStringsToDates(res);
    return res;
} 

编辑这是我为实现而准备的内容

(function() {
    var jsonParse = JSON.parse;
    var reDate = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/i;
    function jsonDate(obj) {
        var type = typeof(obj);
        if(type == 'object') {
            for(var p in obj)
                if(obj.hasOwnProperty(p))
                    obj[p] = jsonDate(obj[p]);
            return obj;
        } else if(type == 'string' && reDate.test(obj)) {
            return new Date(obj);
        } 
        return obj;
    }
    JSON.parse = function(str) { return jsonDate(jsonParse(str)); }
})();
/*
 * Tests
 */
var dt = JSON.parse(JSON.stringify({date: new Date()}));
console.log(typeof(dt.date));
console.log(JSON.parse(JSON.stringify(null)));
console.log(JSON.parse(JSON.stringify(123)));
console.log(JSON.parse(JSON.stringify("test")));
console.log(JSON.parse(JSON.stringify(new Date())));
console.log(JSON.parse(JSON.stringify([1,new Date(),2])));
console.log(JSON.parse(JSON.stringify({d: new Date(), d2: {d3: new Date(), d4: [0,new Date(),4]}})));
于 2013-01-23T20:57:42.983 回答