dotnetbrowser javascript interacting with c# this[string name] - javascript

I have a project that i am running .NET code inside. i need to use the existing objects and cannot modify them.
The class in c# that i am trying to use is
class Fields
{
Hashtable list = new Hashtable();
public Fields this[string Name]
{
get{
blah blah
}
}
}
I am attaching the object to JS window :
window = await _browser.MainFrame.ExecuteJavaScript<IJsObject>("window");
window.Properties["Data"] = _myObject;
when using this in the js environment, i can access everything i need to except i cannot use the getter of the Fields.
window.Data.Fields['123']
Is there something i doing wrong or is there a way to use the getter here?

You can try this approach:
internal class Program
{
public static void Main()
{
using (IEngine engine = EngineFactory.Create())
{
using (IBrowser browser = engine.CreateBrowser())
{
browser.Size = new Size(700, 500);
var myObject = new MyObject();
var window = browser.MainFrame.ExecuteJavaScript<IJsObject>("window").Result;
window.Properties["Data"] = myObject;
// 1. For JS, window.Data.Fields['123'] and window.Data.Fields[123] are equivalent.
// For C# and VB, this is not the case. To be able to handle numeric indices,
// DotNetBrowser will recognize if the parameter as a numeric index and try to look for
// the indexer with the integer parameter, which is not present in the Fields class.
var value = browser.MainFrame
.ExecuteJavaScript("window.Data.Fields['123']")
.Result;
Console.WriteLine($"\twindow.Data.Fields['123']: {value}");
// 2. Workaround: use the get_Item() method generated during compilation.
// It will accept a string as a parameter and return a proper result.
value = browser.MainFrame
.ExecuteJavaScript("window.Data.Fields.get_Item('123')")
.Result;
Console.WriteLine($"\twindow.Data.Fields.get_Item('123'): {value}");
// 3. The non-numeric index will be recognized as string and the corresponding
// indexer will be used.
value = browser.MainFrame
.ExecuteJavaScript("window.Data.Fields['id']")
.Result;
Console.WriteLine($"\twindow.Data.Fields['id']: {value}");
}
}
Console.WriteLine("Press any key to terminate...");
Console.ReadKey();
}
class Fields
{
readonly Hashtable list = new Hashtable();
public string this[string Name]
{
get
{
return list[Name]?.ToString();
}
}
public void Add(string key, string value)
{
list[key] = value;
}
}
private class MyObject
{
public MyObject()
{
Fields = new Fields();
Fields.Add("123", "Test");
Fields.Add("id", "Test2");
}
public Fields Fields { get; }
}
}

Related

How to make objects in javascript

How can I do this in javascript? (Adding getters and setters properly)
public class User {
private UUID uuid;
private String nickName;
private double coins;
private int level;
private float exp;
public User(UUID uuid) {
this.uuid = uuid;
this.nickName = null;
this.coins = 0D;
this.level = 1;
this.exp = 0F;
}
}
In JavaScript you don't need to create first a class to create an object. A Javascript object is an associative array that can contain also functions:
// example of JavaScript object
const user = {
uuid: "user-identifier",
nickname: null,
// ...
}
Of course you can define a function that returns an object. You can think at that function as a constructor if you want, but it is just a function that returns an object. Function closures allow you to define private variables.
function User (uuid) {
let coins = 0; // Visible only inside this function
return {
uuid: uuid,
nickname: nickname,
getCoins: function () { return coins }
}
}
Inheritance is prototypal in Javascript: you take an object and you create a new object that inherits all its properties.
const parentObject = {/*key-value-pairs*/};
const childObject = Object.create(parentObject);
JavaScript has a new operator that makes it easier to handle inheritance and makes a function look like a class.
Recently also a class statement has been added to JavaScript but it is just sintactic shugar for the prototype pattern.
With that in mind, I suggest you read this article for a thorough introduction to JavaScript objects.

what is equivalent of java interface.class in typescript

writing one java code in typescript. In Java I have code like below:
Class<?> meta = Object.class;
and
meta = Processor.class; // Processor is an interface
So what can be equivalent of .class in Type Script? Specially in interface case, if Processor would be a class, then I can simply remove .class path and this is working for me so far.
Updated Example:
public interface ObjectProcessor {}
public interface StreamCloser {}
public interface Registry {
void register(Object value, Object metadata);
}
public class RegistryImpl implements Registry {
void register(Object value, Object metadata) {}
}
RegistryImpl registry = new RegistryImpl();
Object value = ?; //any value
Class<?> meta = Object.class;
if (value instanceof ObjectProcessor) {
meta = ObjectProcessor.class;
} else if (value instanceof StreamCloser) {
meta = StreamCloser.class;
}
registry.register(value, meta);
The class itself. Unlike Java, classes are functions, and functions are first-class, thus you can pass them around.
const meta = Object;
As Java is statically typed, .class can provide an entry point for reflection and provides you with informations about the class (properties, methods). JS is different.

How to pass Javascript dictionary to controller where C# dictionary value is an object

Imagine this Javascript dictionary:
var things = {};
things['1'] = 10;
things['2'] = 11;
Here's a little bit of ajax code:
$.ajax({
url: '/Controller/Foo',
type: 'POST',
data: {
things: things
},
Here's something that works:
[HttpPost]
public IActionResult Foo(Dictionary<string, int> things)
things will show that 1 maps to 10 and 2 maps to 11.
Here's something that DOES NOT work:
[HttpPost]
public IActionResult Foo(Dictionary<string, object> things)
things will show that 1 maps to null and 2 maps to null.
I can not change the Dictionary types. In reality, that Dictionary is part of a complex object that is used throughout the application (on the C# side of things). What you are looking at is a dumbed-down example.
In addition, JSON.stringify does not help at all. In fact, if I stringify the dictionary I get a count of 0, with no values.
Using C# to express my point, I think the expectation is more this (than what is currently happening):
int x = 5;
object foo = (object)x;
The dictionary was defined like this because one could be doing:
things[key] = 1;
or
things[key] = "string";
that's why it was declared as an object.
I am using ASP.NET Core if that matters (and jquery 3.4.1).
Thank you.
You could custom a DictionaryModelBinder like below :
public class DictionaryModelBinder:IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
throw new ArgumentNullException(nameof(bindingContext));
var result = new Dictionary<string, object> {};
var form = bindingContext.HttpContext.Request.Form;
if (form==null)
{
bindingContext.ModelState.AddModelError("FormData", "The data is null");
return Task.CompletedTask;
}
foreach ( var k in form.Keys){
StringValues v = string.Empty;
var flag = form.TryGetValue(k, out v);
if (flag)
{
result.Add(k, v );
}
}
bindingContext.Result = ModelBindingResult.Success(result);
return Task.CompletedTask;
}
}
Controller :
[HttpPost]
public IActionResult Foo([ModelBinder(BinderType = typeof(DictionaryModelBinder))]Dictionary<string, object> things)
{
// the stuff you want
}
I don't think you can bind to object as a parameter (or Dictionary value type) because:
A complex type must have a public default constructor and public writable properties to bind. When model binding occurs, the class is instantiated using the public default constructor.
I think you could either strongly type your object (which sounds like it's not an option for you) or change your endpoint to take all its relevant properties distinctly as simple types (string, bool, int, etc.).
Reference: Model Binding in ASP.NET Core

How to convert native JS array of CLR types to CLR array with Clearscript

I'm looking at converting our application from using JavaScript.NET (Noesis) to use ClearScript.
Our app contains a large number of user-created Javascript algorithms/expressions for financial calculations - I'd rather avoid having to change those if possible.
Currently with JavaScript.NET many of the user-defined algorithms follow a pattern of creating a JavaScript array containing host types and passing that as a parameter to a function on another host type. With JavaScript.NET that conversion "just works". See code below for what I'm trying to do:
using System;
using Microsoft.ClearScript;
using Microsoft.ClearScript.V8;
namespace ClearscriptPlayground
{
class Program
{
static void Main(string[] args)
{
using (var engine = new V8ScriptEngine())
{
var myClass = new MyClass();;
engine.AddHostObject("instance", myClass);
engine.AddHostType("MyType", HostItemFlags.DirectAccess, typeof(MyType));
engine.Execute(
#"var params = [new MyType('bye', 10),
new MyType('hello', 10)];
instance.SomethingElse(params)");
Console.ReadLine();
}
}
}
public class MyClass
{
// public void SomethingElse(ITypedArray<MyType> foo)
// {
// // Doesn't work.
// }
// public void SomethingElse(MyType[] foo)
// {
// // Doesn't work.
// }
// public void SomethingElse(dynamic foo)
// {
// var mapped = foo as ITypedArray<MyType>; // Doesn't work
// var mapped = foo as MyType[]; // Doesn't work either
// }
public void SomethingElse(ScriptObject foo)
{
// Works, but how best to convert to MyType[] or similar?
}
}
public struct MyType
{
public string Foo;
public int Bar;
public MyType(string foo, int bar)
{
Foo = foo;
Bar = bar;
}
}
}
NB: I know that I can create a host array using params = host.newArr(MyType, 2); and that will work - but that would mean modifying all the user-maintained JavaScript which I'd really rather avoid.
You can use the JavaScript array directly via dynamic:
public void SomethingElse(dynamic foo)
{
var length = foo.length;
for (var i = 0; i < length; ++i)
{
var my = (MyType)foo[i];
Console.WriteLine("{0} {1}", my.Foo, my.Bar);
}
}

How can I pass a proper method reference in so Nashorn can execute it?

I am trying to write a library that will let me execute JSON Logic rules via the Nashorn Javascript engine.
My problem right now is specifically around the JSObject wrapper I've created to handle moving data from Java/Kotlin into the scripting engine.
If an array is passed in such as [true] it is wrapped and the json-logic script will receive it, see that it is an array, and attempt to run the following bit of code:
if(Array.isArray(logic)) {
return logic.map(function(l) {
return jsonLogic.apply(l, data);
});
}
When the .map function is called, Nashorn will call getMember("map") on my object expecting to get a function back that it can execute.
This is where I am stuck. I have not been able to find any sort of proper syntax to give Nashorn a method or method reference that can be invoked by it as the receiver of its map function.
The code is available here: https://github.com/deinspanjer/json-logic-java
There are some basic unit tests including the one that exhibits the problem, JavaJsonLogicTest.simpleApplyJEJO().
The line of code that is broken is com/jsonlogic/JSObjectWrappers.kt:97.
I would very much appreciate your help.
UPDATE:
Based on the accepted answer, here is the working Kotlin version of the code:
package com.jsonlogic
import jdk.nashorn.api.scripting.AbstractJSObject
import jdk.nashorn.api.scripting.JSObject
import java.util.function.Function
import javax.script.ScriptEngineManager
fun main(args: Array<String>) {
val m = ScriptEngineManager()
val e = m.getEngineByName("nashorn")
// The following JSObject wraps this list
val l = mutableListOf<Any>()
l.add("hello")
l.add("world")
l.add(true)
l.add(1)
val jsObj = object : AbstractJSObject() {
override fun getMember(name: String?): Any? {
if (name == "map") {
// return a functional interface object - nashorn will treat it like
// script function!
return Function { callback: JSObject ->
val res = l.map {
// call callback on each object and add the result to new list
callback.call(null, it)
}
// return fresh list as result of map (or this could be another wrapper)
res
}
} else {
// unknown property
return null
}
}
}
e.put("obj", jsObj)
// map each String to it's uppercase and print result of map
e.eval("print(obj.map(function(x) '\"'+x.toString()+'\"'))");
}
JSObject.getMember can return any script "callable". That could be another JSObject that returns 'true' for isFunction or a Java functional interface object. Couple of simple Java sample programs here:
import javax.script.*;
import jdk.nashorn.api.scripting.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");
// The following JSObject wraps this list
List<String> l = new ArrayList();
l.add("hello");
l.add("world");
JSObject jsObj = new AbstractJSObject() {
#Override
public Object getMember(String name) {
// return a "function" object for "map"
if (name.equals("map")) {
return new AbstractJSObject() {
#Override
public Object call(Object thiz, Object... args) {
// first argument is the callback passed from script
JSObject callable = (JSObject)args[0];
List<Object> res = new ArrayList<>();
for (Object obj : l) {
// call callback on each object and add the result to new list
res.add(callable.call(null, obj));
}
// return fresh list as result of map (or this could be another wrapper)
return res;
}
#Override
public boolean isFunction() { return true; }
};
} else {
// unknown property
return null;
}
}
};
e.put("obj", jsObj);
// map each String to it's uppercase and print result of map
e.eval("print(obj.map(function(x) x.toUpperCase()))");
}
}
The above example returns a callable JSObject for "map" property. The returned "function" itself uses a callback function as argument. All script functions (and objects) are passed as JSObjects to Java code and so 'map' code casts first argument to JSObject to invoke the script callback function.
The above sample modified to use a functional interface is as follows:
import javax.script.*;
import jdk.nashorn.api.scripting.*;
import java.util.*;
import java.util.function.*;
public class Main2 {
public static void main(String[] args) throws Exception {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");
// The following JSObject wraps this list
List<String> l = new ArrayList();
l.add("hello");
l.add("world");
JSObject jsObj = new AbstractJSObject() {
#Override
public Object getMember(String name) {
if (name.equals("map")) {
// return a functional interface object - nashorn will treat it like
// script function!
return (Function<JSObject, Object>)callback -> {
List<Object> res = new ArrayList<>();
for (Object obj : l) {
// call callback on each object and add the result to new list
res.add(callback.call(null, obj));
}
// return fresh list as result of map (or this could be another wrapper)
return res;
};
} else {
// unknown property
return null;
}
}
};
e.put("obj", jsObj);
// map each String to it's uppercase and print result of map
e.eval("print(obj.map(function(x) x.toUpperCase()))");
}
}
Hope the above samples will help you in coming up with Kotlin version for your scenario.

Categories