Подтвердить что ты не робот

Как перенести унаследованные объекты в JSON?

json2.js, кажется, игнорирует членов родительского объекта при использовании JSON.stringify(). Пример:

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

Вывод:

4
{"someVal":50}

Я бы ожидал этого вывода:

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

Ответ 1

Хорошо, что так оно и есть, JSON.stringify не сохраняет какие-либо свойства, не принадлежащие объекту. Вы можете посмотреть интересную дискуссию о других недостатках и возможных обходных решениях здесь.

Также обратите внимание, что автор не только документировал проблемы, но и написал библиотеку под названием HydrateJS, которая может вам помочь.

Проблема немного глубже, чем кажется на первый взгляд. Даже если a действительно будет привязано к {"position":0, "someVal":50}, тогда его разбор будет создавать объект, который имеет желаемые свойства, но не является экземпляром Actor и не имеет прототипа ссылки на WorldObject (в конце концов, синтаксический анализ метод не имеет этой информации, поэтому он не может восстановить его таким образом).

Чтобы сохранить цепочку прототипов, нужны умные трюки (например, используемые в HydrateJS). Если это не то, к чему вы стремитесь, возможно, вам просто нужно "сгладить" объект, прежде чем его подкрепить. Чтобы сделать это, вы можете, например, итерации всех свойств объекта, независимо от того, являются ли они собственными или нет и повторно назначают их (это гарантирует, что они будут определены самим объектом, а не просто унаследованы от прототипа).

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

Способ записи функции не изменяет исходный объект. Поэтому, используя

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

вы получите нужный результат, а a останется прежним.

Ответ 2

Другим вариантом было бы определить метод toJSON в прототипе объекта, который вы хотите сериализовать:

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

Это работает, так как JSON.stringify ищет метод toJSON в объекте, который он получает, перед тем, как попробовать собственную сериализацию.

Ответ 3

Проверьте эту скрипту: http://jsfiddle.net/AEGYG/

С помощью этой функции вы можете плоский стягивать объект:

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

Ответ 4

В то время как подход flatten вообще работает, фрагменты в других ответах, опубликованных до сих пор, не работают для свойств, которые не изменяются, например, если прототип был замороженное. Чтобы справиться с этим случаем, вам нужно будет создать новый объект и назначить свойства этому новому объекту. Поскольку вы просто стягиваете результирующий объект, объектный идентификатор и другие внутренние элементы JavaScript, вероятно, не имеют значения, поэтому совершенно нормально возвращать новый объект. Этот подход также более читабельен, чем переназначение свойств объекта самому себе, поскольку он не выглядит как no-op:

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

Ответ 5

Вот рекурсивная версия фрагмента @TomasVana, включенная в его ответ, в случае наследования на нескольких уровнях дерева объектов:

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

И он хранит массивы в виде массивов. Назовите его так же:

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