Como restringir objects herdados ao JSON?

json2.js parece ignorar membros do object pai ao usar JSON.stringify (). Exemplo:

require('./json2.js'); function WorldObject(type) { this.position = 4; } function Actor(val) { this.someVal = 50; } Actor.prototype = new WorldObject(); var a = new Actor(2); console.log(a.position); console.log(JSON.stringify(a)); 

A saída é:

 4 {"someVal":50} 

Eu esperaria esta saída:

 4 {"position":0, "someVal":50} 

Bem, é exatamente assim, JSON.stringify não preserva nenhuma das propriedades não pertencentes ao object. Você pode dar uma olhada em uma discussão interessante sobre outras desvantagens e possíveis soluções alternativas aqui .

Observe também que o autor não apenas documentou os problemas, mas também escreveu uma biblioteca chamada HydrateJS que pode ajudá-lo.

O problema é um pouco mais profundo do que parece à primeira vista. Mesmo se realmente fosse a string para {"position":0, "someVal":50} , então analisá-lo mais tarde criaria um object que possui as propriedades desejadas, mas não é uma instância de Ator, nem um link protótipo para O WorldObject (afinal de contas, o método de análise não tem essa informação, então não é possível restaurá-lo dessa maneira).

Para preservar a cadeia de protótipos, são necessários truques inteligentes (como os usados ​​no HydrateJS). Se isso não é o que você está procurando, talvez você só precise “achatar” o object antes de o restringir. Para fazer isso, você poderia, por exemplo, iterar todas as propriedades do object, independentemente de elas serem próprias ou não e reatribuí-las (isso garantirá que elas sejam definidas no próprio object, em vez de apenas herdadas do protótipo).

 function flatten(obj) { var result = Object.create(obj); for(var key in result) { result[key] = result[key]; } return result; } 

A maneira como a function é escrita não altera o object original. Então usando

 console.log(JSON.stringify(flatten(a))); 

você obterá a saída desejada e a permanecerá a mesma.

Outra opção seria definir um método toJSON no protótipo do object que você deseja serializar:

 function Test(){} Test.prototype = { someProperty: "some value", toJSON: function() { var tmp = {}; for(var key in this) { if(typeof this[key] !== 'function') tmp[key] = this[key]; } return tmp; } }; var t = new Test; JSON.stringify(t); // returns "{"someProperty" : "some value"}" 

Isso funciona desde que JSON.stringify procura um método toJSON no object que recebe, antes de tentar a serialização nativa.

Verifique este violino: http://jsfiddle.net/AEGYG/

Você pode simplificar o object usando esta function:

 function flatStringify(x) { for(var i in x) { if(!x.hasOwnProperty(i)) { // weird as it might seem, this actually does the trick! - adds parent property to self x[i] = x[i]; } } return JSON.stringify(x); } 

Aqui está uma versão recursiva do snippet @TomasVana incluído em sua resposta, caso haja inheritance em vários níveis da sua tree de objects:

 var flatten = function(obj) { if (obj === null) { return null; } if (Array.isArray(obj)) { var newObj = []; for (var i = 0; i < obj.length; i++) { if (typeof obj[i] === 'object') { newObj.push(flatten(obj[i])); } else { newObj.push(obj[i]); } } return newObj; } var result = Object.create(obj); for(var key in result) { if (typeof result[key] === 'object') { result[key] = flatten(result[key]); } else { result[key] = result[key]; } } return result; } 

E mantém matrizes como matrizes. Chame da mesma maneira:

 console.log(JSON.stringify(flatten(visualDataViews))); 

Enquanto a abordagem flatten em geral funciona, os fragments em outras respostas postadas até agora não funcionam para propriedades que não são modificáveis, por exemplo, se o protótipo tiver sido congelado . Para lidar com esse caso, você precisaria criar um novo object e atribuir as propriedades a esse novo object. Como você está apenas restringindo o object resultante, a identidade do object e outras partes internas do JavaScript provavelmente não importam, portanto, é perfeitamente correto retornar um novo object. Essa abordagem também é discutivelmente mais legível do que reatribuir as propriedades de um object para si mesmo, já que não parece um não operacional:

 function flatten(obj) { var ret = {}; for (var i in obj) { ret[i] = obj[i]; } return ret; }