I'm learning JavaScript and I was looking for a while about this and I have not got any answer about this. My question is if there is any rule to define a JSON key in JavaScript.
For example, in python there is a rule defining dict and is All the keys must be of an immutable data type such as strings, numbers, or tuples.
var json = {};
json[""] = "White space";
json[" "] = "Two white space";
var emptyJSON = {};
var emptyArray = [];
function a (){}
json[a] = "Function";
json[emptyJSON] = "Json";
json[emptyArray]= "Array";
//I don't know why this property whit an empty array does not appear when I console the json
console.log("Print the entire object =>", json);
//But it appears when I console the specific property
console.log("Print empty array property =>", json[emptyArray]);
Object keys are strings. Anything that is not a string is converted to a string.
var obj = {};
obj[1] = 1;
console.log(obj["1"]);
obj[{}] = 2;
console.log(obj["[Object object]"]);
obj[[1, 2]] = 3;
console.log(obj["1,2"]);
Related
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);
alert(json);
What is the correct way of JSON'ing this array?
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);
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"]}"
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);
};
})();
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;
});
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.
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});
suppose i receive JSON object from the server as this
{ "asdf[zxcv]": "qwer" }
how do i access asdf, zxcv, and qwer in javascript, so i can use the object this way ?
theobj.asdf[zxcv] = 'qwer'
Bracket notation is not in the JSON RFC. You can only read it as string.
var simpleObj = {
"simpleKey": "simpleValue"
}
console.log(simpleObj)
var advObj = {
"advKey[1]": "advValue"
}
console.log(JSON.parse(advObj)); // SyntaxError
console.log(advObj.advKey[1]) // TypeError
console.log(advObj["advKey[1]"]) // can only read as string
You would need to refactor the source JSON into something more meaningful so you can access the values in regular JavaScript way.
Run the following snippet to check how you can solve the issue:
var x = '{ "asdf[zxcv]": "qwer" }';
var y = JSON.parse(x);
var result = Object.keys(y).reduce(function(result, key) {
var parentKey = key.substring(0, key.indexOf("["));
var innerKey = /[a-z]+\[([a-z]+)\]/i.exec(key)[1];
if (!result.hasOwnProperty(key))
result[parentKey] = {};
result[parentKey][innerKey] = y[key];
return result;
}, {});
document.getElementById("structure").textContent = JSON.stringify(result);
var zxcv = result["asdf"]["zxcv"];
document.getElementById("someValue").textContent = zxcv;
<h2>Refactored data structure as nested objects:</h2>
<div id="structure"></div>
<h2>Accessing some value: result["asdf"]["zxcv"] or result.asdf.zxcv</h2>
<div id="someValue"></div>
It's all about creating nested objects to represent the associative keys in the source JSON properties representing a conceptual associative array...
This is one of the way to access all elements without reconstructing object.
jQuery.each(JSON.parse('{ "asdf[zxcv]": "qwer" }'), function(index, value) {
var i = index;// i = "asdf[zxcv]"
var v = value;// v = "qwer"
var iOfInnerValue = (/\[(.*?)\]/g).exec(i)[1];// innerValue = "zxcv"
var iOfOuterValue = index.replace("["+(/\[(.*?)\]/g).exec(i)[1]+"]",""); // outerValue = "asdf"
});
You'll need to assign the data to a variable and then you can use Object keys to get the key which is the part before the :. Here's an example.
var j = { "asdf[zxcv]": "qwer" };
console.log(Object.keys(j)); //["asdf[zxcv]"]
console.log(j); //{asdf[zxcv]: "qwer"}
I'm taking charge of a javascript webapp. It's very complex, and I'm having some trouble with syntax:
getThemeBaseUrl = function() {
var customConfigPath = "./customer-configuration";
if (parseQueryString().CustomConfigPath) {
customConfigPath = parseQueryString().CustomConfigPath;
}
var clientId = parseQueryString().ClientId;
return customConfigPath + "/themes/" + clientId;
};
parseQueryString = function() {
var result = {}, queryString = location.search.substring(1), re = /([^&=]+)=([^&]*)/g, m;
while ( m = re.exec(queryString)) {
result[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
}
return result;
};
in particular parseQueryString().CustomConfigPath and the var result = {}, queryString = location.search.substring(1), re = /([^&=]+)=([^&]*)/g, m;
The first seems to be a sort of property access by the parseQueryString function.
The second seems an array declaration, but without the Array() constructor. Also, the m value is recalled without the presumed array result in the while cycle.
By looking at:
parseQueryString().CustomConfigPath
you can say that parseQueryString() is expected to return an object with CustomConfigPath property.
And from this:
var result = {};
you see that result is indeed an object ({} is an empty object literal). It is not an array. Later, in a loop, there is:
result[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
so we're assigning properties to the result object. One of this properties will be (as we can expect) a CustomConfigPath. This will be taken from the query string - we'll use regular expression to do this: re = /([^&=]+)=([^&]*)/g. So address of the webpage on which this code is executed looks like: http://example.com/something?SomeKey=value&CustomConfigPath=something.
General syntax for assigning properties to an object is:
result[key] = value;
// key -> decodeURIComponent(m[1])
// value -> decodeURIComponent(m[2])
parseQueryString().CustomConfigPath calls the parseQueryString function, which returns an object. Then it accesses the CustomConfigPath property of that object. A common idiom for the first 4 lines of the function is:
var customConfigPath = parseQueryString().CustomConfigPath || "/.customer-configuration";
var result = {}, queryString = location.search.substring(1), re = /([^&=]+)=([^&]*)/g, m is a declaration of 4 different variables, not an array:
result is an empty object
queryString is the query string from the current URL, with the ? removed.
re is a regular expression
m is a variable that isn't initialized, it will be assigned later in the while loop.
I had already functioning code.
Some part of the code should change two values of queryParams array:
myParam1 = 'sort_by';
myParam2 = 'sort_order';
queryParams[myParam1] = 'title';
if (queryParams[myParam2] == 'ASC') {
queryParams[myParam2] = 'DESC';
} else { //DESC or undefined
queryParams[myParam2] = 'ASC';
}
queryPairs = [];
for (var index in queryParams) {
queryPairs.push(index + "=" + queryParams[index]);
}
Unfourtunately some logic has changed and now sometimes queryParams has length 0 at the beginning of this script and then this part failes.
queryParams[myParam1] = 'title';
and
queryParams[myParam2] = 'DESC'
lines do not change queryParams.length,so the length is still 0 and the loop
for (var index in queryParams){
do not work as expected.
I have not found how to add key/value into the array.
You're being a bit confused by the fact that JavaScript's standard arrays aren't really arrays at all.
You're not using queryParams as an array in the code you've quoted. You're using it as an object. Objects don't have a length property by default.
You haven't said what changed, so it's difficult to help you, but note that even if queryParams is an array and you add a property to it as you are, the length will remain 0. The length of an array only relates to a special class of properties (ones that meet the definition of an "array index"), not to all properties.
Some examples that may help:
var a = []; // a is an array
console.log(a.length); // "0"
a['foo'] = 'bar';
console.log(a.length); // "0", the `foo` property doesn't have any effect on `length`
a[0] = 'zero';
console.log(a.length); // "1", the `0` property *does* have an effect on `length`
var o = {}; // o is an object
var myParam1 = 'sort_by';
var myParam2 = 'sort_order';
var propertyName;
o[myParam1] = 'foo';
o[myParam2] = 'DESC';
for (propertyName in o) {
console.log(propertyName); // "sort_by" and then "sort_order" (the order is not defined)
}
I'm trying to stringify an object but don't know why it is not working as expected:
function request(url) {
this.url = url;
this.head = [];
}
var r = new request("http://test.com");
r.head["cookie"] = "version=1; skin=new";
r.head["agent"] = "Browser 1.0";
document.write(JSON.stringify(r));
I hope this object can be stringified as:
{"url":"http://test.com","head":["cookie":"version=1; skin=new", "agent":"Browser 1.0"]}
But I only get:
{"url":"http://test.com","head":[]}
How to fix it?
You want the hear property of r to be an associative array I think (like in PHP). They don't exist in JavaScript. Array's have values that are indexed by a number.
Since r.head is an object (array is object in JS) you can add properties to it with r.head["whatever property name"]="value" but these properties don't seem to be serialized to JSON when you use JSON.stringify because r.head is defined as an array and it'll only serialize the numbered index values.
To fix this you can define r.head as an object so JSON.stringify will serialize all properties.
function request(url) {
this.url = url;
this.head = {};
}
var r = new request("http://test.com");
r.head["cookie"] = "version=1; skin=new";
r.head["agent"] = "Browser 1.0";
document.write(JSON.stringify(r));
If you run the following code in your cosole (press F12 in your browser) you'd see that arrays are not serialized in the same way as objects are:
var b = [];
b.something=22
console.log(b.something);
console.log(JSON.stringify(b));//=[]
console.log(b.hasOwnProperty("something"))//=true
b = {};
b.something=22
console.log(b.something);
console.log(JSON.stringify(b));//={"something":22}
console.log(b.hasOwnProperty("something"))//=true
It would be impossible to serialize it the way you are hoping to.
With this object:
function request(url) {
this.url = url;
this.head = [];
}
This variation:
var r = new request("http://test.com");
r.head.push({"cookie": "version=1; skin=new"});
r.head.push({"agent": "Browser 1.0"});
document.write(JSON.stringify(r));
would give you:
{"url":"http://test.com","head":[{"cookie":"version=1; skin=new"},{"agent":"Browser 1.0"}]}
If you were to change the object to:
function request(url) {
this.url = url;
this.head = {};
}
var r = new request("http://test.com");
r.head["cookie"] = "version=1; skin=new";
r.head["agent"] = "Browser 1.0";
document.write(JSON.stringify(r));
would give you:
{"url":"http://test.com","head":{"cookie":"version=1; skin=new","agent":"Browser 1.0"}}
The first variation is guaranteed to give you the head values in order when you iterate it. It also has the advantage in that you can later insert things in specific order if that is of interest.
The second version, by convention, will give you the items back in the order they were inserted as long as there are no number based keys, but that ordering is not guaranteed by the ecma spec.