Reputation: 1943
I'm serializing quite complex data-structures using native JSON.stringify of chrome(will switch to some serialization library later for cross-browser support). Most of the data can be looked upon as static, but some is dynamic.
When constructing the data I'd like to be able to do something like
var dynamicString=new String();
{a:"foo", b:dynamicString}
Then set the primitive string-value of dynamicString later on. I'd like to do this rather than changing the property "b" of that object because this would allow for constructing this data a lot easier. I.e. something like:
I've already found out the primitive value of a String-object can't be changed. I've also found out that the valueOf-function can be set to a custom function, and by returning the desired value in that function this can basically be achieved if doing things like:
var dynamicString=new String("bar");
a.valueOf=function(){return ("foo")};
alert (a+"bar");//foobar
The problem is that the serialization apparently doesn't use the valueOf-function. Serializing the dynamicString-object in this case puts "bar" in the string returned by JSON.stringify, rather than "foo" which is desired.
One way of getting what I'm after is recursively looping though all the objects of the data-structure prior to serialization, and replacing dynamicData-objects by the primitive value they return. But I'd like to avoid that if possible.
Any suggestions?
Edit: I could of course also put an object in the data, which I also have a reference to. Then I can set the data of that object "dynamically", which will be reflected in the result-string. But that adds another level in the structure. I just want a string, not an object with a string-property.
Upvotes: 1
Views: 81
Reputation: 664620
I've also found out that the valueOf-function can be set to a custom function, and by returning the desired value in that function this can basically be achieved if doing things like:
var a = new String("bar"); a.valueOf = function(){return ("foo")}; console.log(a+"bar"); // "foobar"
Yet, if you overwrite valueOf
it does not make any sense to use a String
object (with some different internal value). Use a plain object with a valueOf
function, or even with just a property which is changed dynamically. With your current approach, you're getting
console.log(a.concat("bar")); // "barbar"!
The problem is that the serialization apparently doesn't use the valueOf-function. Serializing the dynamicString-object in this case puts "bar" in the string returned by JSON.stringify, rather than "foo" which is desired.
Yes. JSON.stringify
does call the toJSON
method on objects, which in your case still returns the internal value. If you overwrite that, it will work:
a.toJSON = a.valueOf;
console.log(JSON.stringify(a)); // '"foo"'
As said above, using native String
objects doesn't make much sense. Instead, we should create an extra constructor for them:
function DynamicString(value) {
if (!(this instanceof DynamicString))
return new DynamicString(value);
this.set(value);
}
var p = DynamicString.prototype = Object.create(String.prototype, {
constructor: {value: DynamicString, configurable: true}
});
p.set = function(value) {
this.__value__ = String(value);
};
p.valueOf = p.toString = p.toJSON = function() {
return this.__value__;
};
Now, using it:
var a = new DynamicString("foo");
console.log(a+"bar"); // "foobar"
console.log(JSON.stringify({a:a, b:"bar"})); // '{"a":"foo","b":"bar"}'
You even got all the String.prototype
methods on it (yet they will yield primitive strings, not dynamic ones).
Upvotes: 1