Clox
Clox

Reputation: 1943

Is there a way of specifying what data of an object is passed to the serializer when it's being serialized?

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

Answers (2)

Bergi
Bergi

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

Halcyon
Halcyon

Reputation: 57729

Use the 2nd parameter to JSON.stringify: replacer

You can also use the toJSON property, it's behaves like a toString.

Upvotes: 1

Related Questions