2013-01-23 14 views
9

Sto tentando di deserializzare un oggetto json che contiene una data javascript. Quando viene chiamato JSON.stringify sull'oggetto, le date vengono serializzate su stringhe che non vengono deserializzate correttamente fino alle date. Ho tentato di deserializzare l'oggetto utilizzando sia l'implementazione nativa del browser con chrome, IE e FF e utilizzando jquery. Entrambi danno i risultati. Ecco lo snippet:Javascript Deserializzazione JSON

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

Voglio objDeser.Date essere una data js non una stringa. Puoi vedere questo problema in azione qui: http://jsbin.com/unijud/24/edit. C'è qualche libreria js che può deserializzare correttamente le date quando si costruisce l'oggetto javascript?

risposta

5

ho preso consigli @LastCoder e ha scritto una semplice implementazione. Sembra che stia facendo ciò che volevo.

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

Un esempio live è disponibile su jsbin. Un riferimento è disponibile su gist.

3

Le specifiche JSON non includono la formattazione speciale per le date. Come tali sono spesso serializzati come una stringa, a volte con marcature speciali per indicare che dovrebbe essere trattato come un oggetto Date se la lingua li supporta. Pertanto, la maggior parte (tutti?) Dei parser JSON nativi del browser non possono eseguire correttamente il round-trip di un oggetto Date.

Ci sono diverse librerie buone che aiutano con questo - mi piace molto MomentJS anche se ho usato datejs nel passato pure. Dovresti solo scorrere gli oggetti e convertire i campi corretti in oggetti Date dopo che sono stati analizzati.

Trovo utile ricordare che il formato JSON è molto più restrittivo della notazione letterale dell'oggetto JavaScript.

+0

Grazie. Queste librerie sembrano aggiungere funzionalità alle date ma non sembrano migliorare/implementare un parser json. – mdeangelo272

0

È possibile aggiungere manualmente tutte le funzioni Date necessarie a 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()); 

Oppure si potrebbe ignorare JSON.parse e farlo scorrere l'oggetto risultato alla ricerca di stringhe che corrispondono al regex tempo timbro e poi convertirli in oggetti Date.

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

EDIT Ecco quello che mi piacerebbe mettere insieme per un'implementazione

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

L'opzione 2 è essenzialmente ciò che sto cercando. Speravo di trovare una libreria che implementasse la funzionalità findAndConvertStringsToDates. Non sono un vero guru e speravo di non doverlo implementare da solo. – mdeangelo272

+0

@ mdeangelo272 - Ho modificato con una soluzione praticabile, potrebbero esserci problemi con i browser molto vecchi, ma meno di 20 righe di codice non sono troppo elaborate. –

+1

Per chiunque trovi questa risposta in futuro, questa è una pessima idea. Questo modifica il 'JSON' globale, quindi tutte le librerie sulla pagina che utilizzano il JSON globale otterranno questa versione con patch delle scimmie che si comporta in modo non standard. Se si desidera questo comportamento, non aggiungerlo all'oggetto JSON globale, ma utilizzare una funzione separata per esso. – bcherny

7

JSON.parse ha un secondo parametro poco conosciuto: la funzione "reviver". Questo è usato proprio per questo scopo: far rivivere una stringa di data in un oggetto Date (o, ipoteticamente, qualsiasi altro tipo di oggetto che si desidera convertire da una stringa) durante l'analisi iniziale.

C'è un SO post su questo, ed ecco una blog post che include un esempio di implementazione e una funzione che farà di proprietà controllo per un paio di data comune codifiche (ISO & che strano .NET formato AJAX), prima di analisi ad un Date.

Ecco la funzione del tasto da quel post sul blog, 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); 

(ci sono circa lots of opinions regex adeguate per ISO 8601 date YMMV..Inoltre, non vi è alcun motivo particolare per assegnare la funzione all'oggetto JSON globale. Potresti memorizzare/referenziare ovunque tu voglia.

+0

Legenda regexp della data ISO migliore: '/^\ d {4} - (0 [1-9] | 1 [0-2]) - ([12] \ d | 0 [1-9] | 3 [01]) ([T \ s] (([01] \ d | 2 [0-3]) \: [0-5] \ d | 24 \: 00) (\: [0-5] \ d ([\. ,] \ d +)) ([ZZ] | ([\ + -]) ([01] \ d |???? 2 [0-3]) \ :? ([0-5] \ d))) ? $/' – asdfasdfads

0

Per rappresentare le date utilizzando JavaScript, ho trovato che JSON utilizza ISO 8601, un formato di stringa specifico per codificare le date come stringa. Quando ho controllato l'ultima volta, non esiste uno standard ufficiale per il formato della data. I principali browser utilizzano ISO 8601 come formato di codifica Data JSON.

Quindi, le date sono codificate come stringhe ISO 8601 e quindi utilizzate come normali stringhe quando il JSON viene serializzato e deserializzato.

Detto questo, le date ISO possono essere convertite in date JavaScript utilizzando il costruttore JavaScript Date, che accetta un'ampia varietà di input per costruire una data, ISO 8601 è uno di questi.

Get data odierna:

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

analizzare in una stringa:

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

Poi riconvertirlo in una data javascript, utilizzando il costruttore:

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