Javascript JSON Date 反序列化

Javascript JSON Date Deserialization

本文关键字:反序列化 Date JSON Javascript      更新时间:2023-09-26

我正在尝试反序列化一个包含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 对象时正确反序列化日期?

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

有一篇关于这个的SO文章,这里有一篇博客文章,其中包括一个实现示例和一个函数,该函数将在解析为Date之前对几个常见的日期编码(ISO和奇怪的.NET AJAX格式)进行属性检查。

这是该博客文章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 日期的正确正则表达式有很多意见。扬子晚报.此外,没有特别的理由将函数打到全局 JSON 对象上。您可以在任何您喜欢的地方存储/引用它。)

我采纳了@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上有一个活的例子。有关要点的参考资料。

为了使用

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

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

话虽如此,ISO 日期可以通过使用 JavaScript Date 构造函数转换为 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)

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

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

我发现记住 JSON 格式比 JavaScript 对象文字表示法更具限制性很有帮助。

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
}

您可以手动将所需的所有 Date 函数添加到 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]}})));