Ограничить глубину строчки JSON

При создании объекта с использованием JSON.stringify (или что-то подобное) существует способ ограничить глубину строения, т.е. только идти n уровней глубоко в дерево объектов и игнорировать все, что приходит после этого (или лучше: помещать заполнители в там, указывая, что что-то не было)?

Я знаю, что JSON.stringify принимает функцию-заменитель формы function (key, value), но я не нашел способ получить глубину в исходном объекте текущей пары ключ-значение, переданной функции замены.

Есть ли способ сделать это с реализацией по умолчанию JSON.stringify? Или я дошел до точки, где я должен был просто реализовать строение самостоятельно? Или есть еще одна библиотека строкообразования, которую вы можете порекомендовать, которая имеет этот параметр?

Ответы

Ответ 1

Я хотел упорядочить объект на первом уровне без добавления сторонней библиотеки/слишком большого количества кода.

Если вы ищете то же самое, вот быстрый однострочный для этого:

var json = JSON.stringify(obj, function (k, v) { return k && v && typeof v !== "number" ? (Array.isArray(v) ? "[object Array]" : "" + v) : v; });

У объекта верхнего уровня не будет ключа, поэтому он всегда просто возвращается, однако все, что не является "числом" на следующем уровне, будет приведено к строке вкл. особый случай для массивов, иначе они будут раскрыты еще больше.

Если вам не нравится этот особый случай массива, пожалуйста, используйте мое старое решение, которое я тоже улучшил:

var json = JSON.stringify(obj, function (k, v) { return k && v && typeof v !== "number" ? "" + v : v; }); // will expose arrays as strings.

Передача массива вместо объекта на верхнем уровне, тем не менее, будет работать в обоих решениях.

Примеры:

var obj = {
  keyA: "test",
  keyB: undefined,
  keyC: 42,
  keyD: [12, "test123", undefined]
}
obj.keyD.push(obj);
obj.keyE = obj;

var arr = [12, "test123", undefined];
arr.push(arr);

var f = function (k, v) { return k && v && typeof v !== "number" ? (Array.isArray(v) ? "[object Array]" : "" + v) : v; };
var f2 = function (k, v) { return k && v && typeof v !== "number" ? "" + v : v; };

console.log("object:", JSON.stringify(obj, f));
console.log("array:", JSON.stringify(arr, f));
console.log("");
console.log("with array string cast, so the array gets exposed:");
console.log("object:", JSON.stringify(obj, f2));
console.log("array:", JSON.stringify(arr, f2));

Ответ 2

Сделайте глубокий клон вашего объекта (с библиотекой, такой как low-dash), делайте то, что когда-либо обрезаете, вы хотите сделать затем передайте его в JSON.stringify. Я бы не стал заново изобретать JSON.stringify, это попытка не в том месте.

[EDIT] выглядит так, как будто кто-то уже сделал то, что вы предлагали: JSON.stringify глубокие объекты

Я бы не рекомендовал этого, потому что родной JSON.stringify всегда будет быстрее и надежнее

[EDIT] вот библиотека, которая, кажется, делает то, что вы хотите: http://philogb.github.io/jit/static/v20/Docs/files/Core/Core-js.html# $jit.json.prune

Ответ 3

Вот функция, которая учитывает встроенные правила JSON.stringify() а также ограничивает глубину:

function stringify(val, depth, replacer, space) {
    depth = isNaN(+depth) ? 1 : depth;
    function _build(key, val, depth, o, a) { // (JSON.stringify() has it own rules, which we respect here by using it for property iteration)
        return !val || typeof val != 'object' ? val : (a=Array.isArray(val), JSON.stringify(val, function(k,v){ if (a || depth > 0) { if (replacer) v=replacer(k,v); if (!k) return (a=Array.isArray(v),val=v); !o && (o=a?[]:{}); o[k] = _build(k, v, a?depth:depth-1); } }), o||{});
    }
    return JSON.stringify(_build('', val, depth), null, space);
}

Как это устроено:

  1. _build() вызывается рекурсивно для построения вложенных объектов и массивов до требуемой глубины. JSON.stringify() используется для итерации каждого непосредственного свойства каждого объекта в соответствии со встроенными правилами. 'undefined' всегда возвращается внутренним заменителем, поэтому JSON на самом деле еще не создан. Помните, что при первом вызове внутреннего заменителя ключ становится пустым (это элемент, подлежащий строковому форматированию).
  2. JSON.stringify() вызывается для окончательного результата для получения фактического JSON.

Пример:

var value={a:[12,2,{y:3,z:{q:1}}],s:'!',o:{x:1,o2:{y:1}}};

console.log(stringify(value, 0, null, 2));
console.log(stringify(value, 1, null, 2));
console.log(stringify(value, 2, null, 2));

{}

{
  "a": [
    12,
    2,
    {}
  ],
  "s": "!",
  "o": {}
}

{
  "a": [
    12,
    2,
    {
      "y": 3,
      "z": {}
    }
  ],
  "s": "!",
  "o": {
    "x": 1,
    "o2": {}
  }
}

(версию, которая обрабатывает циклические ссылки, см. здесь: fooobar.com/info/130183/... - включает версию TypeScript)