This project is read-only.

How to return javascript objects to .net?

Jun 10, 2013 at 2:25 AM
Edited Jun 10, 2013 at 9:57 PM
How would I return the array to .net?
v8Engine.WithContextScope = () =>
            {
                string script = @"
                    var db = {};
                    db.collection = [];
                    db.collection.push({ firstname: "Tom", lastname: "Smith" });
                    db.collection.push({ firstname: "John", lastname: "Velaz" });

                    function GetAll(){
                        return db.collection;
                    }

                    GetAll()
                ";
                Handle result = v8Engine.Execute(script, "Console");
            };
Jun 10, 2013 at 8:04 PM
Edited Jun 10, 2013 at 8:06 PM
Hi, sorry for the delay (I never get email alerts from this site yet for some reason). :/
Handle myArray = (Handle)v8Engine.DynamicGlobalObject.db;
This gives you a handle to the array. Everything is in a handle. All operations are done on the handles returned.

Once you have a handle, you can treat the handle like a variable (there's implicit casting implemented).

Now for objects (which includes arrays), you need to wrap the handle with a managed object:
var myObj = v8Engine.GetObject(myArray);
There are both "named" and "indexed" object property getter and setter methods you can call using the managed wrapper. The extra step here is a speed increase - you only create managed wrappers if you need them. ;)
Jun 10, 2013 at 8:09 PM
Edited Jun 10, 2013 at 8:15 PM
Sorry, just noticed you are returning the result. In that case you can use "result" in place of "myArray" in the bottom example. ;)

Actually, I'll probably implement an implicit conversion to the V8NativeObject base type for handles. Normally "Handle.ManagedObject" should also work, but I just noticed it won't work as expected right now. I will have these in the next update coming out very soon!
Jun 10, 2013 at 9:00 PM
Edited Jun 10, 2013 at 9:57 PM
Now I'm getting the error. "An object handle type is required (such as a JavaScript object or function handle)."

This is what I have so far.
var v8Engine = new V8Engine();
            v8Engine.WithContextScope = () =>
            {
                string script = @"
                    var db = {};
                    db.collection = [];
                    db.collection.push({ firstname: 'Tom', lastname: 'Smith' });
                    db.collection.push({ firstname: 'John', lastname: 'Velaz' });

                    function GetAll(){
                        return db.collection;
                    }

                    GetAll()
                ";
                Handle result = v8Engine.Execute(script, "Console");
                var myObj = v8Engine.GetObject(result);

      
            };
Jun 10, 2013 at 9:25 PM
Edited Jun 10, 2013 at 9:29 PM
Can you put a breakpoint on the line just after "result" and verify the handle type?

I can't test on the working version I have because the handles are streamlined to operate much faster now, so it may work on mine (some bugs may have been fixed in all the changes). They should still operate the same as I mentioned above however.

If testing goes well, I may have another release tonight. I'll be sure to test the above scenario as well.
Jun 10, 2013 at 9:37 PM
This is what I get.
Image
Jun 10, 2013 at 10:00 PM
Looks good, "IsArray" is true, and the length is 2. For some reason the system is not recognizing it as a handle to an object type (which it is). Is the "IsObjectType" property == true?
Jun 10, 2013 at 10:21 PM
IsObjectType == false so is IsObject.
Jun 10, 2013 at 10:41 PM
Edited Jun 10, 2013 at 10:43 PM
Opps, sorry, forgot some types. Ok, if you are compiling the source the fix is here (in the Handles.cs file):
        /// <summary>
        /// Returns true of the handle represents ANY object type.
        /// </summary>
        public bool IsObjectType
        {
            get
            {
                return ValueType == JSValueType.BoolObject
                    || ValueType == JSValueType.NumberObject
                    || ValueType == JSValueType.StringObject
                    || ValueType == JSValueType.Function
                    || ValueType == JSValueType.Date // << just added
                    || ValueType == JSValueType.RegExp // << just added
                    || ValueType == JSValueType.Array // << just added
                    || ValueType == JSValueType.Object;
            }
        }
If not, don't worry, I'll try to get the new release out tonight which has these fixes and more.
Jun 11, 2013 at 3:14 AM
I'll just wait for the new release. Thanks!!
Jun 12, 2013 at 4:02 AM
Almost there, just a few bugs to work out in the new handle system. Hopefully this will be completed asap.
Jun 12, 2013 at 4:05 AM
Awesome!
Jun 17, 2013 at 5:56 AM
New release posted. It's much faster, and I've tested it with your code above - works now. ;)

Thanks for helping make this better!

James
Jun 17, 2013 at 2:17 PM
Sweet!
Jun 17, 2013 at 3:34 PM
Ok I got it to return the first object in the array and pulled the firstname property out.
var v8Engine = new V8Engine();
            v8Engine.WithContextScope = () =>
            {
                string script = @"
                    var db = {};
                    db.collection = [];
                    db.collection.push({ FirstName: 'Tom', LastName: 'Smith' });
                    db.collection.push({ FirstName: 'John', LastName: 'Velaz' });

                    function GetAll(){
                        return db.collection;
                    }

                    GetAll()
                ";
                Handle result = v8Engine.Execute(script, "Console");
                var myObj = v8Engine.GetObject(v8Engine.GetObject(result).GetProperty(0)).DynamicObject;
                var name = myObj.FirstName;
               
            };
If I have a class in .NET that mimics the object is there a way to cast it to that object?

Lets say we use this class.
        public class Person
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
        }
I tried this but just returned a null.
var myObj = v8Engine.GetObject(v8Engine.GetObject(result).GetProperty(0)).DynamicObject as Person;
I could stringify the returning javascript array and then use Newton.Json library to cast it to that Person's class.
But that felt a little hacky.
Jun 17, 2013 at 5:15 PM
Edited Jun 17, 2013 at 5:49 PM
Just inherit from "V8NativeObject":
public class Person : V8NativeObject
{
        public string FirstName { get; set; }
        public string LastName { get; set; }
}

var myObj = v8Engine.GetObject<Person>(v8Engine.GetObject(result).GetProperty(0));
var myDynObj = (DynamicObject)myObj;
;)

The properties are not auto mapped however. To access the properties via script, read on ...
Jun 17, 2013 at 5:36 PM
Edited Jun 17, 2013 at 5:53 PM
That said, there are two ways you can do this: using V8NativeObject, or V8ManagedObject. "V8ManagedObject" allows storing and reading properties on the managed side, but "V8NativeObject" is a bit faster and requires you to register properties using the given methods.

Here are the two ways:
public class Person : V8NativeObject
{
        public string FirstName { get; set; }
        public string LastName { get; set; }

        public void Initialize()
        {
           SetAccessor("FirstName", (this, propertyName)=> { return FirstName; }, (this, propertyName, value)=> { FirstName = value; }, ... );
        }
}
or
public class Person : V8ManagedObject
{
        public string FirstName { get; set; }
        public string LastName { get; set; }

        public void Initialize() { }

        public override InternalHandle NamedPropertyGetter(ref string propertyName)
        {
             if (propertyName == "FirstName") return Engine.CreateNativeValue(FirstName);
             else return base.NamedPropertyGetter(ref propertyName); // *** DONT FORGET THIS ;) ***
        }
}
This is just from memory, so I haven't tested it before posting.

Note: You need to create an "ObjectTemplate" instance ({V8Engine}.CreateObjectTemplate()) and call "{ObjectTemplate}.CreateObject<Person>()" on it in order to create "V8ManagedObject" objects.

BTW: Thanks for these examples - I'll put them in the docs at some point and make them a bit more clear. ;)
Jun 17, 2013 at 5:39 PM
I'm starting to think returning as a json string and then serializing using Newton.Json library might be easier with less code.
What do you think of that technique?
Jun 17, 2013 at 5:57 PM
Edited Jun 17, 2013 at 5:59 PM
Sure, that would definitely be faster when transferring data between the script environment and the managed side, however for debug purposes, it may be easier down the road if you create a "Persons" object that wraps the functionality of returning a collection of "Person" objects - that way you can use the C# debugger. ;)

Perhaps somethine like:
public class Persons: V8NativeObject
{
    public IEnumerable<Person> PersonCollection { /* loop using "yield return" */ }
}
Jun 17, 2013 at 6:06 PM
When I get home this evening, I will write up some sample code in the console project (just a V8.NET "playground") to showcase your code above.