Вопрос или проблема
Кажется, я что-то упускаю, но следующий код (Fiddle) возвращает пустую строку:
var test = new Array();
test['a'] = 'test';
test['b'] = 'test b';
var json = JSON.stringify(test);
console.log(json);
Какой правильный способ преобразования этого массива в строку?
Массивы JavaScript предназначены для хранения данных с числовыми индексами. Вы можете добавлять именованные свойства в них, потому что массив — это тип объекта (и это может быть полезно, когда вы хотите сохранить метаданные о массиве, который содержит нормальные, упорядоченные данные с числовыми индексами), но это не то, для чего они предназначены.
Тип данных массива JSON не может иметь именованные ключи в массиве.
Когда вы передаете массив JavaScript в JSON.stringify
, именованные свойства будут проигнорированы.
Если вам нужны именованные свойства, используйте объект, а не массив.
const test = {}; // Объект
test.a="test";
test.b = []; // Массив
test.b.push('item');
test.b.push('item2');
test.b.push('item3');
test.b.item4 = "A value"; // Игнорируется JSON.stringify
const json = JSON.stringify(test);
console.log(json);
Хорошее объяснение и пример выше. Я нашел это (Странности JSON.stringify() с Prototype.js) для завершения ответа. Некоторые сайты реализуют свой собственный toJSON с JSONFilters, так что удалите его.
if(window.Prototype) {
delete Object.prototype.toJSON;
delete Array.prototype.toJSON;
delete Hash.prototype.toJSON;
delete String.prototype.toJSON;
}
это работает нормально, и вывод теста:
console.log(json);
Результат:
"{"a":"test","b":["item","item2","item3"]}"
Я опубликовал исправление для этого здесь
Вы можете использовать эту функцию, чтобы модифицировать JSON.stringify
, чтобы кодировать массивы
, просто поместите ее ближе к началу вашего скрипта (см. ссылку выше для получения более подробной информации):
// Обновление для JSON.stringify, обновлено для поддержки массивов
(function(){
// Преобразовать массив в объект
var convArrToObj = function(array){
var thisEleObj = new Object();
if(typeof array == "object"){
for(var i in array){
var thisEle = convArrToObj(array[i]);
thisEleObj[i] = thisEle;
}
}else {
thisEleObj = array;
}
return thisEleObj;
};
var oldJSONStringify = JSON.stringify;
JSON.stringify = function(input){
if(oldJSONStringify(input) == '[]')
return oldJSONStringify(convArrToObj(input));
else
return oldJSONStringify(input);
};
})();
Другой подход — это JSON.stringify()
функция замещения. Вы можете передать 2-й аргумент в JSON.stringify()
, который имеет специальное обращение для пустых массивов, как показано ниже.
const arr = new Array();
arr.answer = 42;
// {"hello":"world","arr":{"answer":42}}
JSON.stringify({ hello: 'world', arr }, function replacer(key, value) {
if (Array.isArray(value) && value.length === 0) {
return { ...value }; // Преобразует пустой массив с строковыми свойствами в простой объект
}
return value;
});
В качестве альтернативы, вы можете использовать это
var test = new Array();
test[0]={};
test[0]['a'] = 'test';
test[1]={};
test[1]['b'] = 'test b';
var json = JSON.stringify(test);
alert(json);
Таким образом, вы преобразуете массив в JSON.
Да, массивы JavaScript не ассоциативные как массивы php, они предназначены для хранения данных с числовыми индексами. Но я понимаю, что вы хотите. Массивы JavaScript могут содержать как объектные свойства (например, “строковые индексы”), так и числа в строках (например, “числовые индексы”). Вы можете преобразовать/сериализовать их только в простой объект JavaScript {"key1": value1, "key2": value2, ...}
.
Например, вы хотите преобразовать массив a
, но избежать null/undefined в выводе (для ясности вопрос расширен):
a = []; // новый массив
a[0] = "UK";
a[1] = 7;
a["5"] = 8;
a["32"] = 77;
a[7] = 0;
a["8"] = NaN;
a[9] = undefined;
a["10"] = null;
a[11] = {};
a["19"] = false;
a[20] = true;
a['A'] = 'test';
a['B'] = 'test B';
a.push('wonderland');
a.push(98);
a[4294967294] = 42;
delete a[4294967294];
(4294967295) ['UK', 7, пусто × 3, 8, пусто, 0, NaN, undefined, null, {…}, пусто × 7, false, true, пусто × 11, 77, 'wonderland', 98, пусто × 4294967260, A: 'test', B: 'test B']
a.length
4294967295
Поэтому даже JSON.stringify(a)
может быть возможным, это закончится многими null (и без ‘A’, ‘B’)…
'["UK",7,null,null,null,8,null,0,null,null,null,{},null,null,null,null,null,null,null,false,true,null,null,null,null,null,null,null,null,null,null,null,77,"wonderland",98,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,...
Мы можем это исправить с помощью Object.assign!
function replacer (key, value) {
return (!!value || value === false || value === '' || typeof value === 'number') ? value : undefined;
}
JSON.stringify(Object.assign({}, a), replacer)
Или даже проще:
JSON.stringify(Object.assign({}, a))
'{"0":"UK","1":7,"5":8,"7":0,"8":null,"10":null,"11":{},"19":false,"20":true,"32":77,"33":"wonderland","34":98,"A":"test","B":"test B"}'
Обратное преобразование в массив сложнее:
jsonObj = JSON.stringify(Object.assign({}, a));
aObj = JSON.parse(jsonObj)
b = []
Object.entries(aObj).forEach(entry => b[entry[0]] = entry[1])
console.log(b)
(35) ['UK', 7, пусто × 3, 8, пусто, 0, null, пусто, null, {…}, пусто × 7, false, true, пусто × 11, 77, 'wonderland', 98, A: 'test', B: 'test B']
П. С. 1: Вы также можете видеть, что мы отфильтровали null/undefined для чрезвычайно длинного исходного массива (разреженный массив) a
, что было невозможно с помощью array.filter.
П. С. 2: Но для нашей специфической задачи мы не можем использовать JSON.stringify(Object.assign([], a))
, потому что не хотим упустить a['A'] = 'test'; a['B'] = 'test B'
.
П. С. 3: Чтобы избежать использования таких неинтуитивных вещей, вы должны использовать простой объект {}
или лучше Map
.
JSON должен содержать пары ключ-значение. Однако вы все равно можете иметь массив в качестве части значения. Поэтому добавьте “ключ” на ваш выбор:
var json = JSON.stringify({whatever: test});
.
Ответ или решение
Почему JSON.stringify не работает с обычными массивами JavaScript?
Согласно вашему вопросу, вы столкнулись с проблемой при использовании функции JSON.stringify
для сериализации массива, в котором используются строковые индексы вместо числовых. Давайте разберем суть проблемы.
Проблема с массивами JavaScript
В JavaScript массивы предназначены для хранения данных с числовыми индексами. Хотя вы можете добавить свойства с именованными ключами к массивам (поскольку массивы являются подтипом объектов), это не соответствует их основной концепции. Если вы попытаетесь сериализовать такую структуру с помощью JSON.stringify
, функция не будет учитывать эти именованные свойства.
В приведенном вами примере кода:
var test = new Array();
test['a'] = 'test';
test['b'] = 'test b';
var json = JSON.stringify(test);
console.log(json); // Вывод: ""
JSON.stringify
возвращает пустую строку, поскольку в обычных массивах, содержимое которых было определено с именованными индексами, игнорируется.
Решения
Для корректного использования JSON.stringify
, рекомендуется использовать объект вместо массива для хранения данных с именованными ключами. Вот как можно переписать ваш код:
const test = {}; // Используем объект
test.a = 'test';
test.b = 'test b';
var json = JSON.stringify(test);
console.log(json); // Вывод: '{"a":"test","b":"test b"}'
Альтернативные подходы
Если вы все же хотите использовать массивы, можно рассмотреть несколько альтернативных подходов:
-
Использование вложенных объектов:
Вместо того чтобы пытаться использовать строковые индексы, вы можете использовать индексы массивов и заполнять их объектами:var test = []; test[0] = { key: 'a', value: 'test' }; test[1] = { key: 'b', value: 'test b' }; var json = JSON.stringify(test); console.log(json); // Вывод: '[{"key":"a","value":"test"},{"key":"b","value":"test b"}]'
-
Используйте функцию замены в JSON.stringify:
Если вы хотите обеспечить специальную обработку для ваших массивов, вы можете использовать вторым аргументом дляJSON.stringify
функцию замены (replacer function):const arr = []; arr.a = 'test'; arr.b = 'test b'; var json = JSON.stringify({ arr }, function(key, value) { if (Array.isArray(value)) { return Object.assign({}, value); // Конвертирует массив в объект } return value; }); console.log(json); // Вывод: '{"arr":{"0":"test","1":"test b"}}'
Заключение
В заключение, при работе с JavaScript и JSON.stringify
важно помнить, что массивы не предназначены для хранения именованных ключей. Если вам необходимо использовать именованные индексы, лучше всего воспользоваться объектами. Это обеспечит не только корректное поведение при сериализации, но и сделает ваш код более понятным и читаемым.
Не забывайте использовать правильные структуры данных в зависимости от задачи, и вы избежите многих распространенных ошибок.