Experiment in Evaluating JavaScript with C#

I’m writing a Windows application for one of my projects. One of the data sources for this application is JavaScript code, so I have to either parse or execute the JavaScript code in order to get the information I need. I figured the best easiest way was to execute the code since Microsoft’s JScript engine is built-in (and is a .NET language).

After a quick search, I found a blog post by Rick Strahl documenting his experimentation with evaluating JavaScript code with C#. According to his post, evaluating JavaScript within a .NET application requires the use of the VsaEngine class in the Microsoft.JScript.Vsa namespace (you have to add a reference to the Microsoft.JScript and Microsoft.Vsa components). He provided some simple examples, but none included the evaluation of arrays (and that’s what I needed). So I began digging into the Object Explorer for the Microsoft.JScript namespace and started experimenting.

I wrote a simple class for evaluating JavaScript (it’s a little different from Rick’s):

using Microsoft.JScript;
using Microsoft.JScript.Vsa;

public class JSEval
{
    private static VsaEngine _Engine = VsaEngine.CreateEngine();

    public static object Eval(string javaScript)
    {
        return Microsoft.JScript.Eval.JScriptEvaluate(javaScript, _Engine);
    }

    public static ArrayObject EvalToArray(string javaScript)
    {
        return Eval(javaScript) as ArrayObject;
    }
}

The Microsoft.JScript.ArrayObject class represents the JavaScript Array reference type. It inherits Microsoft.JScript.JSObject and thus is enumerable.

My first test was a simple array literal:

string js = "var foo = ['foo', 'bar'];";

Microsoft.JScript.ArrayObject firstArray = JSEval.EvalToArray(js);

foreach (object i in firstArray)
{
    MessageBox.Show(i.ToString()); // numeric index
    MessageBox.Show(firstArray[i].ToString()); // element value
}

This produces exactly what you'd expect. The VsaEngine evaluates the JavaScript code and returns the array, which you can loop through. The first message box displays the numeric value of the index, and the second displays the element's value at that index.

My second test was still simple, except that I assigned the elements individually after the array is assigned. I performed this test because my data is in this form.

string js = "var foo = []; foo[0] = 'foo'; foo[100] = 'bar';";

object secondArray = JSEval.Eval(js);

MessageBox.Show(secondArray.ToString()); // displays bar

The results of this code are almost the same as if you eval()’d JavaScript code within the browser; the final statement value was returned instead of the array. The difference being that in the browser, the foo array exists within the context eval() was called (typically global); I cannot find the foo array object anywhere in the VsaEngine instance (where I assume it would be) after calling the JScriptEvaluate() method. The global scope is available in the engine, and I would prefer to access the global object within the VsaEngine instance to grab a reference to the array. Instead, you have to make JScriptEvaluate() return the array, and two ways come to my mind:

  • Make the last statement reference the array.
  • Define the array, then populate it inside a function.

In the following code, the two strings in ArrayEval() produce the same output.

private void EvalArray(string javaScript)
{
    Microsoft.JScript.ArrayObject array = JSEval.EvalToArray(js);

    MessageBox.Show(array.ToString());

    foreach (object i in array)
    {
        MessageBox.Show(i.ToString()); // numeric index
        MessageBox.Show(array[i].ToString()); // element value
    }
}

private void ArrayEvalTest()
{
    //array as last statement
    string js = "var foo = []; foo[0] = 'foo'; foo[100] = 'bar'; foo;";
    EvalArray(js);

    // function populates array
    string js2 = "var foo = []; (function () { foo[0] = 'foo'; foo[100] = 'bar';})();";
    EvalArray(js2);
}

You’ll get the Array object either way you go.

A word of warning: Unfortunately, VsaEngine is marked as deprecated as of .NET 2.0, and it will be removed without replacement according to MSDN documentation. That’s a shame, as it really came in handy for my project. I can see this as a powerful tool to add JavaScript support to any .NET application.

7/13/2009 2:37:48 PM | Tags: .NET, C#, JavaScript
© 2008 Jeremy McPeak