Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update the jint options to support System.text.json #15449

Merged
merged 14 commits into from
Mar 14, 2024
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text.Json.Nodes;
using Jint;
using Jint.Native;
using Jint.Runtime.Interop;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.FileProviders;

Expand All @@ -19,7 +23,40 @@ public JavaScriptEngine(IMemoryCache memoryCache)

public IScriptingScope CreateScope(IEnumerable<GlobalMethod> methods, IServiceProvider serviceProvider, IFileProvider fileProvider, string basePath)
{
var engine = new Engine();
var engine = new Engine(options =>
{
// Make JsonArray behave like JS array.
options.SetWrapObjectHandler(static (e, target, type) =>
{
if (target is JsonArray)
{
var wrapped = new ObjectWrapper(e, target)
{
Prototype = e.Intrinsics.Array.PrototypeObject
};
return wrapped;
}

return new ObjectWrapper(e, target);
});

options.AddObjectConverter<JsonValueConverter>();

// We cannot access this[string] with anything else than JsonObject, otherwise itw will throw.
options.SetTypeResolver(new TypeResolver
{
MemberFilter = static info =>
{
if (info.ReflectedType != typeof(JsonObject) && info.Name == "Item" && info is PropertyInfo p)
{
var parameters = p.GetIndexParameters();
return parameters.Length != 1 || parameters[0].ParameterType != typeof(string);
}

return true;
}
});
});

foreach (var method in methods)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System.Text.Json;
using System.Text.Json.Nodes;
using Jint;
using Jint.Native;
using Jint.Runtime.Interop;

namespace OrchardCore.Scripting.JavaScript;
public class JsonValueConverter : IObjectConverter
{
public bool TryConvert(Engine engine, object value, out JsValue result)
{
if (value is JsonValue jsonValue)
{
var valueKind = jsonValue.GetValueKind();
switch (valueKind)
{
case JsonValueKind.Object:
case JsonValueKind.Array:
result = JsValue.FromObject(engine, jsonValue);
break;
case JsonValueKind.String:
result = jsonValue.ToString();
break;
case JsonValueKind.Number:
if (jsonValue.TryGetValue<double>(out var doubleValue))
{
result = JsNumber.Create(doubleValue);
}
else
{
result = JsValue.Undefined;
}
break;
case JsonValueKind.True:
result = JsBoolean.True;
break;
case JsonValueKind.False:
result = JsBoolean.False;
break;
case JsonValueKind.Undefined:
result = JsValue.Undefined;
break;
case JsonValueKind.Null:
result = JsValue.Null;
break;
default:
result = JsValue.Undefined;
break;
}
return true;
}
result = JsValue.Undefined;
return false;

}
}
88 changes: 88 additions & 0 deletions test/OrchardCore.Tests/Scripting/ScriptFunctionsTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using System.Text.Json.Nodes;
using OrchardCore.Scripting;
using OrchardCore.Tests.Apis.Context;

namespace OrchardCore.Tests.Scripting;
public class ScriptFunctionsTest
{
[Fact]
public async Task TheScriptingEngineShouldBeAbleToHandleJsonObject()
{
using var context = new SiteContext();
await context.InitializeAsync();
await context.UsingTenantScopeAsync(scope =>
{
var findUser = new GlobalMethod
{
Name = "tryAccessJsonObject",
Method = sp => () =>
{
const string jsonData = """
{
"age":33,
"falseValue":false,
"trueValue":true,
"stringValue":"stringTest",
"employees": {
"type": "array",
"value": [
{
"firstName": "John",
"lastName": "Doe"
},
{
"firstName": "Jane",
"lastName": "Doe"
}
]
}
}
""";

return JObject.Parse(jsonData);
}
};

var scriptingEngine = scope.ServiceProvider.GetRequiredService<IScriptingEngine>();
var scriptingScope = scriptingEngine.CreateScope([findUser], scope.ServiceProvider, null, null);
var result = (bool)scriptingEngine.Evaluate(scriptingScope,
@"var jobj = tryAccessJsonObject();
return jobj.age == 33;
");
Assert.True(result);

result = (bool)scriptingEngine.Evaluate(scriptingScope,
@"var jobj = tryAccessJsonObject();
return jobj.stringValue == ""stringTest"";
");
Assert.True(result);
result = (bool)scriptingEngine.Evaluate(scriptingScope,
@"var jobj = tryAccessJsonObject();
return jobj.employees.type == ""array"" &&
jobj.employees.value[0].firstName == ""John"";
");
Assert.True(result);


var result1 = scriptingEngine.Evaluate(scriptingScope,
@"var jobj = tryAccessJsonObject();
var steps = [];
if(!jobj.falseValue) steps.push(1);
if(jobj.trueValue) steps.push(2);

// falseValue should be false
if(jobj.falseValue == false) steps.push(3);
if(jobj.trueValue == true) steps.push(4);
if(!!jobj.trueValue) steps.push(5);
steps.push(jobj.falseValue);
steps.push(jobj.falseValue.toString());

return steps.join(',')
");
Assert.Equal("1,2,3,4,5,false,false", result1);


return Task.CompletedTask;
});
}
}
Loading