JavaScript Json.stringify replacer converts values to string - javascript

I am using the javascript JSON.stringify function with a replacer (second parameter) to format date values in a certain way:
var s = JSON.stringify(data, function (key, value) {
if (key === "") return value;
if (jQuery.type(value) === "date") return "Date(" + value.getTime() + ")";
return value;
});
I have valid datetime values in my object "data". However, when the replacer function is executed with this value, the datetime value is automatically converted to a string and therefore jQuery.type(value) = "string" and not "date" anymore.
I could simply replace all datetime values in the value-object before I call stringify, but I would prefer not to modify the original data.
Is this how the replacer function should behave or is this a strange feature of IE (I'm using IE9)? How could I solve this problem?

Try
Date.prototype.toJSON = function() {
return "Date(" + this.getTime() + ")";
};
Without the replacer.

Related

Accessing an array inside of JSON object from reviver

I have a JSON object formatted like {"Foo": ["B","A","R"]}
I am trying to access the values of the array like this:
var json = '{"Foo": ["B","A","R"]}';
expression = JSON.Parse(json, function(key, value){
if(key == "Foo"){
console.log(value.length); // logs "3"
console.log(value[1]); // logs "undefined"
}
});
If I ask for the length of value it returns the correct length of the array, but if I ask for the value it returns undefined and I am not quite sure why.There are other values in the JSON that I am able to access just fine, but they are not arrays. Any insight would be appreciated. Thanks!
You should use JSON.parse like this:
var json = '{"Foo":["B","A","R"]}';
var object = JSON.parse(json);
// object is now and object containing the data from 'json'
var expression = object["Foo"][1]; // object["Foo"] refers to the
// value with key "Foo"
(Calling JSON.parse with a callback parameter is an advanced feature for transforming the JSON object, not reading it. In almost all cases, though, you want to use it like the above code, with no callbacks.)
As mentioned in another answer, if you simply want to retrieve the second element of Foo, you can do that easily enough after parsing using standard property access techniques such as obj.Foo[1].
Assuming you really want to use the optional second "reviver" parameter to JSON.parse, you need to return the value from the "reviver" callback;
expression = JSON.Parse(json, function(key, value){
if (key == "Foo"){
console.log(value[1]);
}
return value;
^^^^^^^^^^^^^
});
The reason it appears you can't access value[1] but you can access value.length is (as mentioned by user663031) you don't have a return value.
The reviver function replaces one value with another, if no return is specified all functions will return undefined. The order the reviver receives the values is: first each of the values in the array separately, then the array.
In your code each value has already been replaced with "undefined", so the array has three undefined values as reported by the length. value[1] really is returning the value at position 1 but it is set to "undefined".
When the json string has arrays the reviver function is called with index, [Object] as key, value parameters .
This sniped of code that filter object properties on parse phase will be helpful:
var json = '{"_embedded": [{"a":"A","b":"B","links": {"c":"C"}},{"a":"A2", "b":"B2","links": {"c":"C2"}}]}';
var schemaNames=["_embedded", "a"];
var result= JSON.parse(json, function(k, v) {
console.log(k,v);
if ("" === k){
return v;
}
// On arrays k is a number
else if (schemaNames.indexOf(k) > -1 || !isNaN(k)){
return v;
}
});
console.log(JSON.stringify(result));
Output: {"_embedded":[{"a":"A"},{"a":"A2"}]}
https://jsfiddle.net/pdorgambide/vphbmtk1/
use this code
var json = {'foo' : ['B', 'A', 'R']};
$.each(json, function(key, value){if(key == 'foo'){console.log(value[1]);}});
you already have a json object so no need to parse it again.

Stringifying a regular expression?

Of course, JSON does not support Regex literals.
So,
JSON.stringify(/foo/)
gives:
{ }
Any workarounds?
I think this is the closest you can get:
RegExp.prototype.toJSON = function() { return this.source; };
JSON.stringify({ re: /foo/ }); // { "re": "foo" }
You can pass a a custom replacer function to JSON.stringify and convert the regular expression to a string (assuming that the expression is part of an array or object):
JSON.stringify(value, function(key, value) {
if (value instanceof RegExp) {
return value.toString();
}
return value;
});
If you don't actually want/need to create JSON, just call the toString() method of the expression.
Although JavaScript objects allow you to put regex as values, JSON does not as it is meant to store only data, not code.
As a workaround, you could convert your regular expression to a string using the toString() method.
var str = /([a-z]+)/.toString(); // "/([a-z]+)/"
You can use a replacer function:
JSON.stringify(/foo/,
function(k, v) {
if (v && v.exec == RegExp.prototype.exec) return '' + v;
else return v;
})

Serialization of RegExp

So, I was interested to find that JSON.stringify reduces a RegExp to an empty object-literal (fiddle):
JSON.stringify(/^[0-9]+$/) // "{}"
Is this behavior expected? I realize that a RegExp is an object with no properties to serialize. That said, dates are objects too; yet JSON.stringify() manages to produce a meaningful string:
JSON.stringify(new Date) // "2014-07-03T13:42:47.905Z"
I would have hoped that JSON would give RegExp the same consideration by using RegExp.prototype.toString().
If somebody would be interested, there is a nice workaround. I don't think, that current behaviour is correct. For example, Date instance is not serialized to empty object like RegExp, though it is an object and also has no JSON representation.
RegExp.prototype.toJSON = RegExp.prototype.toString;
// sample
var foo = { rgx: /qux$/ig, date: new Date }
JSON.stringify(foo);
//> {"rgx":"/qux$/gi","date":"2014-03-21T23:11:33.749Z"}"
Both JSON.stringify and JSON.parse can be customized to do custom serialization and deserialization by using the replacer and reviver arguments.
var o = {
foo: "bar",
re: /foo/gi
};
function replacer(key, value) {
if (value instanceof RegExp)
return ("__REGEXP " + value.toString());
else
return value;
}
function reviver(key, value) {
if (value.toString().indexOf("__REGEXP ") == 0) {
var m = value.split("__REGEXP ")[1].match(/\/(.*)\/(.*)?/);
return new RegExp(m[1], m[2] || "");
} else
return value;
}
console.log(JSON.parse(JSON.stringify(o, replacer, 2), reviver));
You just have to come up with your own serialization format.
Yes, because there's no canonical representation for a RegExp object in JSON. Thus, it's just an empty object.
edit — well it's 2018 now; the answers suggesting solutions using .toJSON() etc are probably fine, though I'd add the method to the prototype with
Object.defineProperty(RegExp.prototype, "toJSON", {
value: RegExp.prototype.toString
});
and so on. That ensures that the function name isn't enumerable, which makes the monkey-patch somewhat more hygienic.
Here's how I solved this issue:
Serialize it as a string:
var pattern = /foobar/i;
var serialized = JSON.stringify(pattern.toString());
Then rehydrate it using another regex:
var fragments = serialized.match(/\/(.*?)\/([a-z]*)?$/i);
var rehydrated = new RegExp(fragments[1], fragments[2] || '');
Preserves the pattern and flags - hope this helps someone!
RegExp.prototype.toJSON = RegExp.prototype.toString;
var regexp = /^[0-9]+$/;
var foo = { rgx: regexp.source, date: new Date };
var stringified = JSON.stringify(foo);
new RegExp(JSON.parse(stringified).rgx)
I think a good approach would be something like this:
function stringifyFilter(key,value) {
if (value instanceof RegExp) {
return value.toString();
}
return value;
}
var myObj = {
text : 'Howdy ho!',
pattern : /[a-z]+/i
}
JSON.stringify(myObj,stringifyFilter); // output: {"text":"Howdy ho!","pattern":"/[a-z]+/i"}

How to convert a JSON string into a JavaScript Object including type checking

For a Javascript project I have an json string converted into a Javascript object. But the type of all my values is 'string' becaus of the JSON parsing. Is there any solution to identify the types and let a script convert them into the correct javascript type?
for example
//Javascript object for the json decoded string
var jsonObj = { id: "foo", count: "1" };
All the values are of type 'string' but I want count to be seen as a number. Is there a parser to set the correct type or does it need to be done manual in JS?
You can use a reviver with JSON.parse.
json2.js describes the reviver thus
JSON.parse(text, reviver)
The optional reviver parameter is a function that can filter and
transform the results. It receives each of the keys and values,
and its return value is used instead of the original value.
If it returns what it received, then the structure is not modified.
If it returns undefined then the member is deleted.
So to convert count to a number you might do
JSON.parse(myJsonString, function (key, value) {
return key === "count" ? +value : value;
});
so
JSON.stringify(JSON.parse('{ "id": "foo", "count": "3" }', function (key, value) {
return key === "count" ? +value : value;
}));
produces
{"id":"foo","count":3}
EDIT
To handle dates as well, you can
JSON.parse(myJsonString, function (key, value) {
// Don't muck with null, objects or arrays.
if ("object" === typeof value) { return value; }
if (key === "count") { return +value; }
// Unpack keys like "expirationDate" whose value is represented as millis since epoch.
if (/date$/i.test(key)) { return new Date(+value); }
// Any other rules can go here.
return value;
});

jQuery JSON to string?

Instead of going JSON a json string and using $.parseJSON, I need to take my object and store it in a variable as string representing JSON.
(A library I'm dealing with expects a malformed JSON type so I need to mess around with it to get it to work.)
What's the best way to do this?
Edit: You should use the json2.js library from Douglas Crockford instead of implementing the code below. It provides some extra features and better/older browser support.
Grab the json2.js file from: https://github.com/douglascrockford/JSON-js
// implement JSON.stringify serialization
JSON.stringify = JSON.stringify || function (obj) {
var t = typeof (obj);
if (t != "object" || obj === null) {
// simple data type
if (t == "string") obj = '"'+obj+'"';
return String(obj);
}
else {
// recurse array or object
var n, v, json = [], arr = (obj && obj.constructor == Array);
for (n in obj) {
v = obj[n]; t = typeof(v);
if (t == "string") v = '"'+v+'"';
else if (t == "object" && v !== null) v = JSON.stringify(v);
json.push((arr ? "" : '"' + n + '":') + String(v));
}
return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}");
}
};
var tmp = {one: 1, two: "2"};
JSON.stringify(tmp); // '{"one":1,"two":"2"}'
Code from: http://www.sitepoint.com/blogs/2009/08/19/javascript-json-serialization/
I use
$.param(jsonObj)
which gets me the string.
Most browsers have a native JSON object these days, which includes parse and stringify methods. So just try JSON.stringify({}) and see if you get "{}". You can even pass in parameters to filter out keys or to do pretty-printing, e.g. JSON.stringify({a:1,b:2}, null, 2) puts a newline and 2 spaces in front of each key.
JSON.stringify({a:1,b:2}, null, 2)
gives
"{\n \"a\": 1,\n \"b\": 2\n}"
which prints as
{
"a": 1,
"b": 2
}
As for the messing around part of your question, use the second parameter. From http://www.javascriptkit.com/jsref/json.shtml :
The replacer parameter can either be a function or an array of
String/Numbers. It steps through each member within the JSON object to
let you decide what value each member should be changed to. As a
function it can return:
A number, string, or Boolean, which replaces the property's original value with the returned one.
An object, which is serialized then returned. Object methods or functions are not allowed, and are removed instead.
Null, which causes the property to be removed.
As an array, the values defined inside it corresponds to the names of
the properties inside the JSON object that should be retained when
converted into a JSON object.
The best way I have found is to use jQuery JSON
You could parse the JSON to an object, then create your malformed JSON from the ajavscript object. This may not be the best performance-wise, tho.
Otherwise, if you only need to make very small changes to the string, just treat it as a string, and mangle it using standard javascript.

Categories