2013-01-23 19 views
9

Próbuję deserializować obiekt Json, który zawiera datę javascript. Kiedy wywoływany jest obiekt JSON.stringify na obiekcie, daty są przekształcane do postaci szeregowej na łańcuchy, które nie są poprawnie przekształcane z powrotem na daty. Podjęto próbę deserializacji obiektu przy użyciu zarówno natywnej implementacji przeglądarki z chrome, IE, i FF oraz przy użyciu jquery. Oba dają pewne wyniki. Oto krótki opis:JavaScript JSON Data Deserialization

Chcę, aby obiekt objDeser.Date był datą js a nie ciągiem znaków. Możesz zobaczyć ten problem w akcji tutaj: http://jsbin.com/unijud/24/edit. Czy są jakieś biblioteki js, które mogą poprawnie deserializować daty podczas budowania obiektu javascript?

Odpowiedz

5

Wziąłem @LastCoder rady i napisałem prostą implementację. Wydaje się, że robię to, co chciałem.

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

Przykładem na żywo jest jsbin. Numer referencyjny jest dostępny pod adresem gist.

3

Specyfikacja JSON nie uwzględnia specjalnego formatowania dat. Jako takie są często serializowane jako ciąg znaków, czasem ze specjalnymi oznaczeniami wskazującymi, że powinny być traktowane jako obiekt Date, jeśli dany język je obsługuje. W związku z tym większość (wszystkich?) Rodzimych analizatorów JSON działających w trybie przeglądarek nie może prawidłowo wywoływać obiektu Date.

Jest kilka dobrych bibliotek, które pomagają w tym - bardzo lubię MomentJS chociaż użyłem także datejs w przeszłości. Po prostu trzeba powtórzyć obiekty i przekonwertować odpowiednie pola na obiekty Date po ich przeanalizowaniu.

Uważam, że warto pamiętać, że format JSON jest znacznie bardziej restrykcyjny niż zapis literowy obiektu JavaScript.

+0

Dziękuję. Biblioteki te wydają się dodawać funkcje do dat, ale nie wydają się ulepszać/implementować analizatora składni json. – mdeangelo272

0

Można ręcznie dodać wszystkie wymagane funkcje Date do 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()); 

Albo można zastąpić JSON.parse i mieli je w pętlę przez obiekt wynik patrząc na smyczki pasujących regex znacznika czasu, a następnie konwertować je do tej pory obiektów.

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

EDIT Oto co będę rzucać razem dla implementacji

(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

Opcja 2 jest zasadniczo tym, nad czym się spodziewam. Miałem nadzieję znaleźć bibliotekę, która zaimplementowała funkcję findAndConvertStringsToDates. Nie jestem guru js i miałem nadzieję, że nie będę musiał sam tego realizować. – mdeangelo272

+0

@ mdeangelo272 - Edytowałem z praktycznym rozwiązaniem, mogą występować problemy z bardzo starymi przeglądarkami, ale mniej niż 20 linii kodu nie jest zbyt trudne do wykonania. –

+1

Dla każdego, kto znajdzie tę odpowiedź w przyszłości, jest to bardzo zły pomysł. Modyfikuje to globus "JSON", więc wszystkie biblioteki na stronie, które używają globalnego JSON, otrzymają tę poprawioną małpią wersję, która zachowuje się w niestandardowy sposób. Jeśli chcesz tego zachowania, nie dodawaj go do globalnego obiektu JSON, ale użyj do tego oddzielnej funkcji. – bcherny

7

JSON.parse ma mało znany drugi parametr: funkcję "reviver". Jest to używane dokładnie w tym celu: ożywić łańcuch daty w obiekcie Date (lub hipotetycznie, każdy inny rodzaj obiektu, który chciałbyś przekonwertować z łańcucha) podczas początkowego analizowania.

Jest to SO post na ten temat, a oto blog post który zawiera przykładową implementację i funkcję, która zrobi nieruchomość sprawdzanie Kilka wspólny termin kodowania (ISO & że dziwne .NET Format AJAX), przed analizą do Date.

Oto klucz funkcja z tego blogu, 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); 

(Istnieje lots of opinions o odpowiednich regexes dla ISO 8601 terminach YMMV..Ponadto, nie ma żadnego szczególnego powodu, aby uderzyć funkcję w globalny obiekt JSON. Możesz przechowywać/odwoływać się w dowolnym miejscu.)

+0

Lepsze wyliczenie daty ISO: '/^\ 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

W celu reprezentowania dat przy użyciu JavaScript, stwierdziłem, że JSON używa ISO 8601, określonego formatu ciągu do kodowania dat jako ciąg. Kiedy ostatnio sprawdzałem, nie ma oficjalnego standardu, jak wygląda format daty. Główne przeglądarki używają ISO 8601 jako formatu kodowania daty JSON.

Daty są zakodowane jako ciągi ISO 8601, a następnie używane tak jak zwykłe ciągi, gdy JSON jest serializowany i deserializowany.

W związku z tym daty ISO można przekształcić na daty JavaScript za pomocą konstruktora daty JavaScript, który akceptuje szeroką gamę danych wejściowych do skonstruowania daty, przy czym ISO 8601 jest jednym z nich.

Get data dzisiejsza:

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

przetworzy go na łańcuch:

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

następnie przekształcić go z powrotem do daty JavaScript, używając konstruktora:

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