Sherlock
Sherlock

Reputation: 7597

JSON.stringify doesn't work with normal JavaScript array

I must be missing something here, but the following code (Fiddle) returns an empty string:

var test = new Array();
test['a'] = 'test';
test['b'] = 'test b';
var json = JSON.stringify(test);
console.log(json);

What is the correct way of stringifying this array?

Upvotes: 80

Views: 177474

Answers (7)

Monday
Monday

Reputation: 165

Yes, JavaScript arrays are not associative like php-arrays, they are designed to hold data with numeric indexes. But I understand what you want. JavaScript arrays can contain both object properties (like "string indexes") and numbers in strings (like "number indexes"). You can stringify/serialize it only to plain JavaScript object {"key1": value1, "key2": value2, ...}.

E.g. you want stringify an array a, but avoid null/undefined in output (for clarity question is extended):

a = []; // new Array();
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, empty × 3, 8, empty, 0, NaN, undefined, null, {…}, empty × 7, false, true, empty × 11, 77, 'wonderland', 98, empty × 4294967260, A: 'test', B: 'test B']
a.length
4294967295

So even JSON.stringify(a) could be possible, it will be finished with many nulls (and without '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,...

We can fix it with Object.assign!

function replacer (key, value) {
  return (!!value || value === false || value === '' || typeof value === 'number') ? value : undefined;
}

JSON.stringify(Object.assign({}, a), replacer)

Or even simpler:

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

Back conversion to array is harder:

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, empty × 3, 8, empty, 0, null, empty, null, {…}, empty × 7, false, true, empty × 11, 77, 'wonderland', 98, A: 'test', B: 'test B']

P. S. 1: You can also see, that we filtered null/undefined for extremely long source array (sparse array) a, which was impossible with array.filter.

P. S. 2: But for our specific task, we can not use JSON.stringify(Object.assign([], a)), because we do not want to miss a['A'] = 'test'; a['B'] = 'test B'.

P. S. 3: To avoid using such unintuitive things, you must use Plain Object {} or better Map.

Upvotes: 1

vkarpov15
vkarpov15

Reputation: 3872

Another approach is the JSON.stringify() replacer function param. You can pass a 2nd arg to JSON.stringify() that has special handling for empty arrays as shown below.

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 }; // Converts empty array with string properties into a POJO
  }
  return value;
});

Upvotes: 3

Quentin
Quentin

Reputation: 943579

JavaScript arrays are designed to hold data with numeric indexes. You can add named properties to them because an array is a type of object (and this can be useful when you want to store metadata about an array which holds normal, ordered, numerically indexed data), but that isn't what they are designed for.

The JSON array data type cannot have named keys on an array.

When you pass a JavaScript array to JSON.stringify the named properties will be ignored.

If you want named properties, use an Object, not an Array.

const test = {}; // Object
test.a = 'test';
test.b = []; // Array
test.b.push('item');
test.b.push('item2');
test.b.push('item3');
test.b.item4 = "A value"; // Ignored by JSON.stringify
const json = JSON.stringify(test);
console.log(json);

Upvotes: 138

Adrien
Adrien

Reputation: 375

Nice explanation and example above. I found this (JSON.stringify() array bizarreness with Prototype.js) to complete the answer. Some sites implements its own toJSON with JSONFilters, so delete it.

if(window.Prototype) {
    delete Object.prototype.toJSON;
    delete Array.prototype.toJSON;
    delete Hash.prototype.toJSON;
    delete String.prototype.toJSON;
}

it works fine and the output of the test:

console.log(json);

Result:

"{"a":"test","b":["item","item2","item3"]}"

Upvotes: 13

user4023394
user4023394

Reputation:

Alternatively you can use like this

var test = new Array();
test[0]={};
test[0]['a'] = 'test';
test[1]={};
test[1]['b'] = 'test b';
var json = JSON.stringify(test);
alert(json);

Like this you JSON-ing a array.

Upvotes: 1

user2217603
user2217603

Reputation: 15

Json has to have key-value pairs. Tho you can still have an array as the value part. Thus add a "key" of your chousing:

var json = JSON.stringify({whatver: test});

Upvotes: -7

JVE999
JVE999

Reputation: 3517

I posted a fix for this here

You can use this function to modify JSON.stringify to encode arrays, just post it near the beginning of your script (check the link above for more detail):

// Upgrade for JSON.stringify, updated to allow arrays
(function(){
    // Convert array to object
    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);
    };
})();

Upvotes: 6

Related Questions