Is it possible to embed an EJSON object inside another EJSON object?

In my application, I compiled one coffeescript class inside another (an instance of class A includes an array of objects that have instances of class B as one of its properties). Then I discovered a video about EJSON and thought it would be great to use this with my objects. However, Meteor does not seem to cope with EJSON objects inside other EJSON objects - class A can be stored in the data store and, when requested, is returned as class A , but class B ends back from the collection as an instance of Object , and not class B When I ran some test code, the built-in EJSON objects seemed to work first (after the initial collection.insert() ), but after updating the browser it returned a simple Object or even incorrectly structured objects. My theory is that there is some inconsistency in the behavior of minimongo and server mongo, but there may be other reasons.

So, is there a way to insert one EJSON object inside another? Perhaps there is a flaw in my code? Is this just a bad idea? I could just class A to create class B objects in my constructor , but it looks like EJSON should be able to handle this. If you think this is a mistake, I would love to post a question about github, but I thought I'd check it out first.

If you need code to try this, you can try the code below, which sets up two essentially identical classes, one of which is called Inner , and one is called Outer and creates an Outer instance called Outer that contains an Inner instance as an innerHere property. In the console, type testCollection.insert({outerHere: outer} . Now testCollection.findOne() can provide you with an object in which the object innerHere property is a innerHere Inner instance, but if you refresh the browser, the same command may return something else.

If this question is difficult to understand, let me know and I will try to clarify.

Code for setting this parameter (just create a .coffee file in the new project root directory):

 @testCollection = new Meteor.Collection("test") class @Outer constructor: (value) -> @value = value clone: -> new Outer(@value) equals: (other) -> _.isEqual(@, other) typeName: -> "Outer" toJSONValue: -> value: @value EJSON.addType("Outer", (value)-> new Outer(value) ) class @Inner constructor: (value) -> @value = value clone: -> new Inner(@value) equals: (other) -> _.isEqual(@, other) typeName: -> "Inner" toJSONValue: -> value: @value EJSON.addType("Inner", (value)-> new Inner(value) ) @outer = new Outer({innerHere: new Inner ("inner value")}) 
+4
source share
1 answer

When EJSON calls the JSONValue of your Outer type, it does not recurs to the result to automatically determine the internal types. Similarly, from the JSONValue function (the function that you pass to the EJSON.addType method), you get a JSON value object (the result of whatever is returned by the JSONValue function), and you can do something about it. To better understand the conversion process, go through an example based on your classes.

Let's say we are going to pass an instance of your Outer class through wiring (for example, as a parameter in a method call).

 myOuter = new Outer({innerHere: new Inner('inner value')}); 

The meteor will follow steps similar to:

 EJSON.stringify(myOuter) => var jsonValue = EJSON.toJSONValue(myOuter); var json = JSON.stringify(jsonValue); 

In the EJSON.toJSONValue call, a new object is created with the property $ type and $ value. The value of $ value is the result of calling the JSONValue function on your object. So jsonValue is an object that looks like this:

 { $type: 'Outer', $value: { innerHere: { value: 'inner value' } } } 

Calling JSON.stringify (jsonValue) results in a JSON string that looks like this:

 "{"$type":"Outer","$value":{"value":{"innerHere":{"value":"inner value"}}}}" 

If you want the innerHere property to be an EJSON type, we also need to call EJSON.toJSONValue on this object (from the Outer toJSONValue method). For example (in js):

 Outer.prototype.toJSONValue = function () { return { value: EJSON.toJSONValue(this.value) }; }; 

Now suppose we create a new Outer instance as follows:

 myOuter = new Outer(new Inner('inner value')); 

And then we call EJSON.toJSONValue (myOuter):

 { $type: 'Outer', $value: { value: { $type: 'Inner', $value: { value: 'inner value' } } } } 

And the resulting json string, which is sent over the wire, looks like this:

 "{"$type":"Outer","$value":{"value":{"$type":"Inner","$value":{"value":"inner value"}}}}" 

Ok, now what happens in our JSONValue function? We are going to get an object similar to the one returned by the JSONValue function. Therefore, we need to call EJSON.fromJSONValue for each of the properties, which we know are custom types, before passing them to the Outer constructor. In this example, you can do the following:

 EJSON.addType('Outer', function (jsonValue) { var inner = EJSON.fromJSONValue(jsonValue.value); return new Outer(inner); }); 

Two methods you can use to test serialization and deserialization:

 var serialized = EJSON.stringify(myOuter); var deserialized = EJSON.parse(serialized); // is the resulting object what you expect? 

Hope this helps!

+4
source

Source: https://habr.com/ru/post/1490954/


All Articles