Domanda Archiviazione di oggetti in HTML5 localStorage


Vorrei memorizzare un oggetto JavaScript in HTML5 localStorage, ma il mio oggetto apparentemente viene convertito in una stringa.

Posso archiviare e recuperare i tipi e gli array primitivi di JavaScript usando localStorage, ma gli oggetti non sembrano funzionare. Dovrebbero?

Ecco il mio codice:

var testObject = { 'one': 1, 'two': 2, 'three': 3 };
console.log('typeof testObject: ' + typeof testObject);
console.log('testObject properties:');
for (var prop in testObject) {
    console.log('  ' + prop + ': ' + testObject[prop]);
}

// Put the object into storage
localStorage.setItem('testObject', testObject);

// Retrieve the object from storage
var retrievedObject = localStorage.getItem('testObject');

console.log('typeof retrievedObject: ' + typeof retrievedObject);
console.log('Value of retrievedObject: ' + retrievedObject);

L'output della console è

typeof testObject: object
testObject properties:
  one: 1
  two: 2
  three: 3
typeof retrievedObject: string
Value of retrievedObject: [object Object]

Mi sembra il setItem il metodo sta convertendo l'input in una stringa prima di memorizzarlo.

Vedo questo comportamento in Safari, Chrome e Firefox, quindi presumo che sia la mia incomprensione del HTML5 Web Storage specifica, non un bug o limitazione specifico del browser.

Ho cercato di dare un senso al clone strutturato algoritmo descritto in http://www.w3.org/TR/html5/infrastructure.html. Non capisco perfettamente cosa stia dicendo, ma forse il mio problema ha a che fare con le proprietà del mio oggetto che non sono enumerabili (???)

C'è una soluzione facile?


Aggiornamento: il W3C alla fine cambiò idea riguardo alle specifiche del clone strutturato e decise di cambiare le specifiche per adattarle alle implementazioni. Vedere https://www.w3.org/Bugs/Public/show_bug.cgi?id=12111. Quindi questa domanda non è più valida al 100%, ma le risposte potrebbero comunque essere di interesse.


2050
2018-01-06 04:05


origine


risposte:


Guardando il Mela, Mozilla e Microsoft documentazione, la funzionalità sembra essere limitata a gestire solo coppie chiave / valore stringa.

Una soluzione alternativa può essere stringa i il tuo oggetto prima di memorizzarlo e in seguito analizzarlo quando lo recuperi:

var testObject = { 'one': 1, 'two': 2, 'three': 3 };

// Put the object into storage
localStorage.setItem('testObject', JSON.stringify(testObject));

// Retrieve the object from storage
var retrievedObject = localStorage.getItem('testObject');

console.log('retrievedObject: ', JSON.parse(retrievedObject));

2649
2018-01-06 04:25



Un miglioramento minore su a variante:

Storage.prototype.setObject = function(key, value) {
    this.setItem(key, JSON.stringify(value));
}

Storage.prototype.getObject = function(key) {
    var value = this.getItem(key);
    return value && JSON.parse(value);
}

Per colpa di valutazione del cortocircuito, getObject() volere subito ritorno null Se key non è in Archiviazione. Inoltre non getterà a SyntaxError eccezione se value è "" (la stringa vuota; JSON.parse() non posso gestirlo).


562
2018-06-30 06:45



Potresti trovare utile estendere l'oggetto Storage con questi metodi pratici:

Storage.prototype.setObject = function(key, value) {
    this.setItem(key, JSON.stringify(value));
}

Storage.prototype.getObject = function(key) {
    return JSON.parse(this.getItem(key));
}

In questo modo si ottiene la funzionalità che si voleva veramente anche se sotto l'API supporta solo le stringhe.


195
2018-01-06 04:42



Estendere l'oggetto di archiviazione è una soluzione eccezionale. Per la mia API, ho creato una facciata per localStorage e poi controllo se si tratta di un oggetto o meno durante l'impostazione e la ricezione.

var data = {
  set: function(key, value) {
    if (!key || !value) {return;}

    if (typeof value === "object") {
      value = JSON.stringify(value);
    }
    localStorage.setItem(key, value);
  },
  get: function(key) {
    var value = localStorage.getItem(key);

    if (!value) {return;}

    // assume it is an object that has been stringified
    if (value[0] === "{") {
      value = JSON.parse(value);
    }

    return value;
  }
}

64
2018-01-21 18:29



C'è una grande libreria che avvolge molte soluzioni in modo da supportare anche i browser più vecchi chiamati jStorage

È possibile impostare un oggetto

$.jStorage.set(key, value)

E recuperalo facilmente

value = $.jStorage.get(key)
value = $.jStorage.get(key, "default value")

52
2017-08-23 03:52



Stringify non risolve tutti i problemi

Sembra che le risposte qui non coprano tutti i tipi che sono possibili in JavaScript, quindi ecco alcuni brevi esempi su come gestirli correttamente:

//Objects and Arrays:
    var obj = {key: "value"};
    localStorage.object = JSON.stringify(obj);  //Will ignore private members
    obj = JSON.parse(localStorage.object);
//Boolean:
    var bool = false;
    localStorage.bool = bool;
    bool = (localStorage.bool === "true");
//Numbers:
    var num = 42;
    localStorage.num = num;
    num = +localStorage.num;    //short for "num = parseFloat(localStorage.num);"
//Dates:
    var date = Date.now();
    localStorage.date = date;
    date = new Date(parseInt(localStorage.date));
//Regular expressions:
    var regex = /^No\.[\d]*$/i;     //usage example: "No.42".match(regex);
    localStorage.regex = regex;
    var components = localStorage.regex.match("^/(.*)/([a-z]*)$");
    regex = new RegExp(components[1], components[2]);
//Functions (not recommended):
    function func(){}
    localStorage.func = func;
    eval( localStorage.func );      //recreates the function with the name "func"

Non lo consiglio per memorizzare funzioni perché eval() il male può portare a problemi di sicurezza, ottimizzazione e debug.         In generale, eval() non dovrebbe mai essere usato nel codice JavaScript.

Membri privati

Il problema con l'utilizzo JSON.stringify()per la memorizzazione di oggetti è, che questa funzione non può serializzare membri privati. Questo problema può essere risolto sovrascrivendo il .toString() metodo (che viene chiamato implicitamente quando si memorizzano i dati nella memoria Web):

//Object with private and public members:
    function MyClass(privateContent, publicContent){
        var privateMember = privateContent || "defaultPrivateValue";
        this.publicMember = publicContent  || "defaultPublicValue";

        this.toString = function(){
            return '{"private": "' + privateMember + '", "public": "' + this.publicMember + '"}';
        };
    }
    MyClass.fromString = function(serialisedString){
        var properties = JSON.parse(serialisedString || "{}");
        return new MyClass( properties.private, properties.public );
    };
//Storing:
    var obj = new MyClass("invisible", "visible");
    localStorage.object = obj;
//Loading:
    obj = MyClass.fromString(localStorage.object);

Riferimenti circolari

Un altro problema stringify non può trattare sono riferimenti circolari:

var obj = {};
obj["circular"] = obj;
localStorage.object = JSON.stringify(obj);  //Fails

In questo esempio, JSON.stringify() getterà a TypeError  "Conversione di una struttura circolare in JSON".         Se la memorizzazione di riferimenti circolari deve essere supportata, il secondo parametro di JSON.stringify() potrebbe essere usato:

var obj = {id: 1, sub: {}};
obj.sub["circular"] = obj;
localStorage.object = JSON.stringify( obj, function( key, value) {
    if( key == 'circular') {
        return "$ref"+value.id+"$";
    } else {
        return value;
    }
});

Tuttavia, la ricerca di una soluzione efficiente per la memorizzazione di riferimenti circolari dipende molto dalle attività che devono essere risolte e il ripristino di tali dati non è banale.

Ci sono già alcune domande su SO che affronta questo problema: Stringify (convertire in JSON) un oggetto JavaScript con riferimento circolare


50
2017-11-19 09:51



Utilizzo degli oggetti JSON per l'archiviazione locale:

//IMPOSTATO

var m={name:'Hero',Title:'developer'};
localStorage.setItem('us', JSON.stringify(m));

//OTTENERE

var gm =JSON.parse(localStorage.getItem('us'));
console.log(gm.name);

// Iterazione di tutte le chiavi e i valori di archiviazione locali

for (var i = 0, len = localStorage.length; i < len; ++i) {
  console.log(localStorage.getItem(localStorage.key(i)));
}

// ELIMINA

localStorage.removeItem('us');
delete window.localStorage["us"];

29
2017-11-20 07:06



In teoria, è possibile memorizzare oggetti con funzioni:

function store (a)
{
  var c = {f: {}, d: {}};
  for (var k in a)
  {
    if (a.hasOwnProperty(k) && typeof a[k] === 'function')
    {
      c.f[k] = encodeURIComponent(a[k]);
    }
  }

  c.d = a;
  var data = JSON.stringify(c);
  window.localStorage.setItem('CODE', data);
}

function restore ()
{
  var data = window.localStorage.getItem('CODE');
  data = JSON.parse(data);
  var b = data.d;

  for (var k in data.f)
  {
    if (data.f.hasOwnProperty(k))
    {
      b[k] = eval("(" + decodeURIComponent(data.f[k]) + ")");
    }
  }

  return b;
}

Tuttavia, la serializzazione / deserializzazione delle funzioni non è affidabile perché dipende dall'implementazione.


27
2018-04-05 21:20



Potresti anche ignorare lo spazio di archiviazione predefinito setItem(key,value) e getItem(key) metodi per gestire oggetti / matrici come qualsiasi altro tipo di dati. In questo modo, puoi semplicemente chiamare localStorage.setItem(key,value) e localStorage.getItem(key) come faresti normalmente.

Non l'ho testato estesamente, ma è sembrato funzionare senza problemi per un piccolo progetto con cui ho lavorato.

Storage.prototype._setItem = Storage.prototype.setItem;
Storage.prototype.setItem = function(key, value)
{
  this._setItem(key, JSON.stringify(value));
}

Storage.prototype._getItem = Storage.prototype.getItem;
Storage.prototype.getItem = function(key)
{  
  try
  {
    return JSON.parse(this._getItem(key));
  }
  catch(e)
  {
    return this._getItem(key);
  }
}

22
2018-04-26 13:00



Sono arrivato a questo post dopo aver colpito un altro post che è stato chiuso come duplicato di questo - intitolato "come archiviare un array in localstorage?". Che va bene, ma nessuno dei due thread fornisce una risposta completa su come è possibile mantenere un array in localStorage - tuttavia sono riuscito a creare una soluzione basata sulle informazioni contenute in entrambi i thread.

Quindi, se qualcun altro vuole essere in grado di spingere / muovere / spostare gli elementi all'interno di un array, e vogliono che l'array sia memorizzato in localStorage o effettivamente sessionStorage, qui vai:

Storage.prototype.getArray = function(arrayName) {
  var thisArray = [];
  var fetchArrayObject = this.getItem(arrayName);
  if (typeof fetchArrayObject !== 'undefined') {
    if (fetchArrayObject !== null) { thisArray = JSON.parse(fetchArrayObject); }
  }
  return thisArray;
}

Storage.prototype.pushArrayItem = function(arrayName,arrayItem) {
  var existingArray = this.getArray(arrayName);
  existingArray.push(arrayItem);
  this.setItem(arrayName,JSON.stringify(existingArray));
}

Storage.prototype.popArrayItem = function(arrayName) {
  var arrayItem = {};
  var existingArray = this.getArray(arrayName);
  if (existingArray.length > 0) {
    arrayItem = existingArray.pop();
    this.setItem(arrayName,JSON.stringify(existingArray));
  }
  return arrayItem;
}

Storage.prototype.shiftArrayItem = function(arrayName) {
  var arrayItem = {};
  var existingArray = this.getArray(arrayName);
  if (existingArray.length > 0) {
    arrayItem = existingArray.shift();
    this.setItem(arrayName,JSON.stringify(existingArray));
  }
  return arrayItem;
}

Storage.prototype.unshiftArrayItem = function(arrayName,arrayItem) {
  var existingArray = this.getArray(arrayName);
  existingArray.unshift(arrayItem);
  this.setItem(arrayName,JSON.stringify(existingArray));
}

Storage.prototype.deleteArray = function(arrayName) {
  this.removeItem(arrayName);
}

esempio di utilizzo: memorizzazione di stringhe semplici nell'array localStorage:

localStorage.pushArrayItem('myArray','item one');
localStorage.pushArrayItem('myArray','item two');

esempio di utilizzo: memorizzazione di oggetti nella sessioneStruttura di archiviazione:

var item1 = {}; item1.name = 'fred'; item1.age = 48;
sessionStorage.pushArrayItem('myArray',item1);

var item2 = {}; item2.name = 'dave'; item2.age = 22;
sessionStorage.pushArrayItem('myArray',item2);

metodi comuni per manipolare gli array:

.pushArrayItem(arrayName,arrayItem); -> adds an element onto end of named array
.unshiftArrayItem(arrayName,arrayItem); -> adds an element onto front of named array
.popArrayItem(arrayName); -> removes & returns last array element
.shiftArrayItem(arrayName); -> removes & returns first array element
.getArray(arrayName); -> returns entire array
.deleteArray(arrayName); -> removes entire array from storage

19
2018-05-07 11:35