Consider extending JSON grammar to support objects with circular reference
Wouldn't it be easier to extend JSON to support a getter with a function that refers to "this"?
o={a:123, get b(){return this.a}};o.b
Getter is imperative, it may be lot harder to make it safe; and its semantics would differ whether used in JSON / in eval (can lead to nasty long-hidden bugs). Path is declarative (and fails fast in eval).
If one would want to make it more code-like, then something should be devised which has the same meaning in code as well as in JSON. Like:
{
x:{
a:{
obj:[]
},
b: [-1].a
}
}
where [n] is reserved form (other may be used) to contain stack of nested currently defined literals in the actual lexical scope, possible to index by positive (absolute) as well as negative (relative); so it could also be [0].x.a or [1].a (geez, it can be made even generally to access all nested lexical scopes, to access its local variables if it is code; but then possibly only negative index would be permitted..., it is hard to define where positive should start, at file level, probably).
Herby Vojčík wrote:
Getter is imperative, it may be lot harder to make it safe; and its semantics would differ whether used in JSON / in eval (can lead to nasty long-hidden bugs). Path is declarative (and fails fast in eval).
If one would want to make it more code-like, then something should be devised which has the same meaning in code as well as in JSON. Like:
{ x:{ a:{ obj:[] }, b: [-1].a } }
where [n] is reserved form (other may be used) to contain stack of
Prefix it with some reserved word, like 'circular', otherwise it is simply array literal (I've got a stupid day today)... Like this: { x:{ a:{ obj:[] }, b: circular[-1].a } }
It's a shame that "this" doesn't work with object literals :( How nice would this be:
{ "a":123, "b": this.a }
// path could also point to a none-object value { a:11, b:path(/a) }
How would you build this from a JavaScript object? There is (so far) no way to have a reference to a none-object value, so there would be no way to construct this json object from a JavaScript object, and no way to recontstruct a JavaScript object from this json string. Therefore paths should only be able to refer to objects or arrays.
Marius Gundersen
On Mon, Mar 5, 2012 at 12:45 PM, gaz Heyes <gazheyes at gmail.com> wrote:
It's a shame that "this" doesn't work with object literals :( How nice would this be:
{ "a":123, "b": this.a }
Not nice. Not nice at all.
Try reading: { "a" : this.b, ..... long object .... "b" : 42, ..... more long object .... }
Then try omitting the "b".
And then try {"a" : this.b, "b" : this.a }
Good luck debugging!
JSON literals, as currently designed, can't fail. They always describe a value. All of these suggestions introduces ways to make syntax that doesn't necessarily describe a value. In order to make a reference to another object, you need two things to be in sync, either a tag/ref pair or a path that must match the actual structure.
If you need cyclic structures or DAGs, perhaps JSON is not for you :)
/L 'and JSON isn't controlled by this group anyway'
On 5 March 2012 12:16, 程劭非 <csf178 at gmail.com> wrote:
{ "a":123, "b": this.a }
If you simply want “this” in JSON.parse, it will not be hard to implement it in my library. But I guess the problem is we have no way to refer to its parent. Do you have any ideas?
You could have something like this:
{ "a":123, "b": function circular() { return this.a; } }
and then obj.b(); would result in 123
JSON.stringfyEx will never produce text like this but JSON.parseEx accept this form.
I mean { a:11, b:path(/a) } will act all the same with { a:11, b:11 }
So it would produce copies of literals, but references of objects/arrays?
For example: var obj = {}; obj.b = {}; obj.a = obj.b;
Would produce:
{ "b": {}, "a": path(/b) }
and could be parsed so adding a member to either obj.b or ob.a would update the other one. This would therefore be more powerfull than simply copying the same values to the different names when parsing.
Marius Gundersen
On 5 March 2012 13:20, 程劭非 <csf178 at gmail.com> wrote:
JSON.stringfyEx will never produce text like this but JSON.parseEx accept this form.
I mean { a:11, b:path(/a) } will act all the same with { a:11, b:11 }
Do you expect
{ a: path(a2), a2: {c: 1, d: path(../b/d)}, b: path(b2), b2: {c: path(../a/c), d: 2}, }
to work? Even ignoring the issue with circularities, you would need to do (deep) dependency analysis before initialising individual properties.
{ a: path(/a2), // yes, path(/a2) is a object a2: {c: 1, d: path(../b/d)}, // no, path(/b) is a path itself you will get undefined here. b: path(/b2), //yes, path(/b2) is a object b2: {c: path(../a/c), d: 2}, // no path(../a) is a path itself you will get undefined here. }
In general, I mean a path will never refer to a object specified by a path.
2012/3/5 Andreas Rossberg <rossberg at google.com>
So it would produce copies of literals, but references of objects/arrays?
Yes, it would.
and could be parsed so adding a member to either obj.b or ob.a would
update the other one. This would therefore be >more powerfull than simply
copying the same values to the different names when parsing.
I don't quite sure about what do you mean by “update”. obj.a and obj.b should be the same object. There are no third object in this case.
Consider the following code:
var obj = JSON.parseEx(" {
"b": {},
"a": path(/b)
} ");
obj.a===obj.b would be true. They are the same object.
2012/3/5 Marius Gundersen <gundersen at gmail.com>
On 5 March 2012 13:35, 程劭非 <csf178 at gmail.com> wrote:
{ a: path(/a2), // yes, path(/a2) is a object a2: {c: 1, d: path(../b/d)}, // no, path(/b) is a path itself you will get undefined here. b: path(/b2), //yes, path(/b2) is a object b2: {c: path(../a/c), d: 2}, // no path(../a) is a path itself you will get undefined here. }
In general, I mean a path will never refer to a object specified by a path.
Why? And anyway, what about:
{ a: {c: 1, d: path(../b/d)}, b: {c: path(../a/c), d: 2}, }
You still need deep dependency analysis.
No, not that hard.
When I find a path node what I need to do is only: set the value if the node already exists, create a listener if the node doesn't exist.
When creating a property node(for object) or element node(for array), I just check if there is a listener to this node.
2012/3/5 Andreas Rossberg <rossberg at google.com>
Mozilla used to support something like this, it is being removed in Firefox 12, but perhaps the implementation can give you ideas.
Let's get back to some key mainly non-technical points.
JSON is a data interchange format that was created to support data interchange between heterogeneous systems, not just JavaScript.
TC39 does not define the actual JSON encoding format. If we extend it, it is no longer JSON.
There are many domain specific applications of the JSON format. These typically make use of a data schema defined (either formally or informally) over the JSON format.
One domain specific application is serialization (for storage or transport) of arbitrary JavaScript object graphs. Many of us have defined either special case or general purpose JSON schemas for doing this. Many people have also done so for other languages.
Designing a JS object graph serialization schema for JSON is not the same thing as extending JSON.
If anybody thinks they have the ultimate JS serialization schema, publish it as a library and try to get people to use it. If it gets broad adoption and there is a major benefit that would come from it being an integral part of the language standard, come on back and talk to TC-39.
Finally, I'm not saying there are no circumstances under which we should consider extending the ES JSON support. For example, I think we should consider adding JSON.parseJSONP as a function. JSONP has broad adoption and there would be a major security benefit from safely parsing it at the ES engine level.
You are right, Allen. I didn't mean to modify JSON itself. And as Lasse Reichstein said above, JSON is not controled by this group at all.
I am just thinking we need a way to serialize/unserialize a js object with circular reference to itself.
Obviously JSON can not handle it currently. It might be Object.serialize/Object.unserialize or JSON.parseJSONP or what ever. I'm OK with any form of APIs.
在 2012年3月6日 上午12:53,Allen Wirfs-Brock <allen at wirfs-brock.com>写道:
Yes, assuming that GetBase is usable (8.9) :
var obj = { x:{ a: GetBase(this) // obj } }
But it is an internal function only, there are things defined in specs to access properties of objects but nothing the other way, because I believe the case never happens today.
The "this" proposal is not bad for me (and even good), if I take Lasse Reichstein's objection, I would say :
{"a" : this.b, //undefined "b" : this.a } //undefined
Same as if you do : function f() {this.a = this.b; this.b = this.a}; var g = new f();//g.a undefined //g.b undefined
It does not solve your issue but it makes me think to a more global issue, the "lexical this" here brendaneich.com/2011/01/harmony-of-my-dreams or this post esdiscuss/2012-February/020749 (which apparently did not passionate)
But this should not be applicable to functions only, this could be generalized to objects, where "this" unless explicitely bound to something should refer to the object itself, and not the global object (moreover that there are discussions about the future of the global object)
Then an Object.GetBase could be added to refer to the "parent" or "outer object"
I am not aware of all discussions (maybe it was already discussed and rejected) and it's not easy to see the whole impact of such change, but I don't think that the idea is absurd, I did not invent it myself and it would be more logical than the current behavior of "this" and avoid repetitives operations (var self=this, getters/setters, use of new (why do I have to use new in the example above ?))
A. Vitte
Le 05/03/2012 13:16, ??? a écrit :
Some object could have more than one ancestor.
Yes, so ? It's not in contradiction with what I am saying
Le 06/03/2012 14:58, 程劭非 a écrit :
Even if GetBase was existing I don't know if it would cover all your needs but unless I missed something I don't get your last example (where p is defined and where GetBase is supposed to be called ?)
Le 06/03/2012 16:33, 程劭非 a écrit :
On Mar 5, 2012, at 11:50 AM, 程劭非 wrote:
Hi, everyone,
As you know, JSON API will throw a syntax error when trying to stringify a object with circular reference.But indeed the feature "serializing/unserializing object with circular reference" is needed.(eg. when storing a graph,or status machine)
This could interest you: xk/JASON
JASON is just like JSON, but unlike JSON it can:
• serialize objects with methods
• serialize objects with cyclic references
• understand Dates, Regexps, Booleans, etc, and restore them with .parse() with their proper types/classes.
• understand and serialize all the JS primitives, including undefined
• properly recreate the holes in Arrays
On Mar 5, 2012, at 5:53 PM, Allen Wirfs-Brock wrote:
Finally, I'm not saying there are no circumstances under which we should consider extending the ES JSON support. For example, I think we should consider adding JSON.parseJSONP as a function. JSONP has broad adoption and there would be a major security benefit from safely parsing it at the ES engine level.
Yes, please, that would be awesome. IIRC Crockford proposed ~ that about a century ago (JsonRequest() or something, ISTR)
+1k
程劭非 wrote:
I am just thinking we need a way to serialize/unserialize a js object with circular reference to itself.
Not just circular references, join points as well (anything making the object graph not a tree).
As Allen suggested, with ES6 facilities including WeakMaps, this is best solved via library development and competition. You want github, not Ecma TC39, sorting it out.
2012/3/6 Brendan Eich <brendan at mozilla.org>
程劭非 wrote:
I am just thinking we need a way to serialize/unserialize a js object with circular reference to itself.
Not just circular references, join points as well (anything making the object graph not a tree).
As Allen suggested, with ES6 facilities including WeakMaps, this is best solved via library development and competition. You want github, not Ecma TC39, sorting it out.
This isn't too difficult to do in a library. I've done it, I think Ember.js does it, and I think there's some other more generic library that does it, but I couldn't find it. The way I do it is by a convention where objects have ids and there can be references to those ids. Then I do a JSON.parse and walk the object graph after to do the rest of the magic. Performance hasn't been an issue (at least for me). Its really not that hard to do, and until it gets more widely used, I agree that it doesn't make sense to consider adding to the language.
That will work everywhere, as I mentionned the issue is more global, your case made me think about it, it's only the begining of a suggestion, whether it's nice or not in the representation can be fixed later, what I am questionning here is the abnormal/not optimized behavior of "this", still thinking about it, what I wrote below does not solve all cases
Le 06/03/2012 17:33, 程劭非 a écrit :
everyone,
As you know, JSON API will throw a syntax error when trying to stringify a object with circular reference.But indeed the feature "serializing/unserializing object with circular reference" is needed.(eg. when storing a graph,or status machine)
I am working on a JS implement of parse/stringify to support a “path grammar” in JSON. It looks like the following:
// this will produce a object with it's property “x” reference to it self { x:path(/) }
//also relative path is supported { x:{ a:path(../x), b:path(x) } }
//ref to deeper object { x:{ a:{ obj:[] }, b:path(/x/a/obj) } }
// path could also point to a none-object value { a:11, b:path(/a) }
// path ref to a path node will get undefined, there is no way to recurse { a:path(/), //the root object b:path(/a) //undefined }
( The project is located at wintercn/JSONplus . in which JSON.pareseEx and JSON.stringifyEx passes all test262 API cases except the ones related to circular reference. )
I was wondering if the future version of ECMAScript could support this grammar or something like this to enable serializing/unserializing object with circular reference. It will be a good news for ones working on complex data structure.
PS. Another known way of descripting object with circular reference is using sharp variabledeveloper.mozilla.org/en/Sharp_variables_in_JavaScript
in object literal . But there are several problems:
Thanks, Shaofei Cheng