最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to Enumerate a Dictionary<> in Jint - Stack Overflow

programmeradmin3浏览0评论

I have a .NET Generic Dictionary<> that I want to pass to a JavaScript function which I am running in Jint.

Jint doesn't treat the .NET Dictionary like a JavaScript object which can be treated like a Dictionary. You can access the .NET properties and methods on the object but not the Extension methods.

So while I can get a count of the Dictionary Keys I cannot enumerate it or call ToArray() on it.

I can used dict[key] to read values out of the Dictionary but in this instance I do not know the keys in advance.

How can I enumerate the keys or get all entries in the .NET Generic Dictionary?

I'm open to doing something to the dictionary or converting it before I pass it it in or figuring out how to do it in the JavaScript. I would prefer not to have to pass an array of the keys separately. This is inside another data structure and doing that for each dictionary would make it more plicated but it is one of the options that I'm considering if I can't find another solution.

I would prefer to stay away from using dynamics. I've had trouble with them leaking memory when used heavily in the past.

I have a .NET Generic Dictionary<> that I want to pass to a JavaScript function which I am running in Jint.

Jint doesn't treat the .NET Dictionary like a JavaScript object which can be treated like a Dictionary. You can access the .NET properties and methods on the object but not the Extension methods.

So while I can get a count of the Dictionary Keys I cannot enumerate it or call ToArray() on it.

I can used dict[key] to read values out of the Dictionary but in this instance I do not know the keys in advance.

How can I enumerate the keys or get all entries in the .NET Generic Dictionary?

I'm open to doing something to the dictionary or converting it before I pass it it in or figuring out how to do it in the JavaScript. I would prefer not to have to pass an array of the keys separately. This is inside another data structure and doing that for each dictionary would make it more plicated but it is one of the options that I'm considering if I can't find another solution.

I would prefer to stay away from using dynamics. I've had trouble with them leaking memory when used heavily in the past.

Share Improve this question edited Sep 8, 2017 at 6:44 Rob 27.4k16 gold badges88 silver badges102 bronze badges asked Sep 8, 2017 at 6:32 ZackZack 2,3512 gold badges26 silver badges39 bronze badges 4
  • I've never used jint before, but it looks like your question is how to enumerate members of an object in javascript. Objects in javascript are dictionaries, and are typically serialized as so. This will likely answer your question – Rob Commented Sep 8, 2017 at 6:40
  • You can iterate over javascript object properties like this, however, it is hard to tell exactly what you are wanting to do here. On the side, you can enumerate a Dictionary<T> KeyCollection, and get a list of all keys. – Daniel Park Commented Sep 8, 2017 at 6:42
  • doesn't work in jint. it the object was declared in JavaScript I could use the normal js way of doing it, but its a .NET object passed into a js environment and you can't treat it like a regular js object. I already specifically tried for(var i in mydict) and for(var i in mydict.Keys) neither of which worked. – Zack Commented Sep 8, 2017 at 6:47
  • Jint does not try to resolve extension methods, like ToArray. You can try to roll out your own version of ObjectWrapper that does , but I just tried and it's not trivial (some useful methods are private so you have to copy them) – Titian Cernicova-Dragomir Commented Sep 8, 2017 at 6:58
Add a ment  | 

3 Answers 3

Reset to default 5

I just ran into the same problem and solved it by using the enumerator on the dictionary:

// Javascript in Jint:
var enumerator = myDictionary.GetEnumerator();
while (enumerator.MoveNext()) {
    var key = enumerator.Current.Key;
    var value = enumerator.Current.Value;
    // do stuff with key and/or value
}

The same way you could just iterate myDictionary.Keys or myDictionary.Values if you are not interested in both.

Note: You mentioned you could use dict[key]. At least for me where I had a plex struct as key, this didn't work. Apparently Jint can't handle generic indexers well and throws: System.InvalidOperationException: No matching indexer found.

An simple alternative that might fit your particular situation is to pass the value as JSON instead. There are many cases when this is useful, particularly when writing tests using jint. This adds a dependency on a serializer though, and is probably slower, but very convenient and certainly predictable and debuggable.

using Jint;
using Newtonsoft.Json;

namespace Extensions
{
    public static class EngineExtentions {
        public static void SetJsonValue(this Engine source, string name, object value)
        {
            source.SetValue(name, JsonConvert.SerializeObject(value));
            source.Execute($"{name} = JSON.parse({name})");
        }
    }
}

You could create your own ObjectWrapper that resolves the ToArray method for it's target object. Here's my quick stab at it, you will need to improve things like error handling, but it's a start:

public class ObjectWrapperExt: ObjectInstance, IObjectWrapper
{
    private ObjectWrapper _target;

    public ObjectWrapperExt(Engine engine, Object obj)
        : base(engine)
    {
        _target = new ObjectWrapper(engine, obj);
    }
    public object Target => _target.Target;

    public override PropertyDescriptor GetOwnProperty(string propertyName)
    {

        if (propertyName == "ToArray")
        {
            if (this.Properties.TryGetValue(propertyName, out var prop)) return prop;

            var descriptor = new PropertyDescriptor(new ToArrayFunctionInstance(Engine), false, true, false);
            Properties.Add(propertyName, descriptor);
            return descriptor;
        }

        return _target.GetOwnProperty(propertyName);
    }

    public override void Put(string propertyName, JsValue value, bool throwOnError)
    {
        _target.Put(propertyName, value, throwOnError);
    }

    public class ToArrayFunctionInstance : FunctionInstance
    {
        private static MethodInfo toArray = typeof(Enumerable).GetMethod("ToArray", BindingFlags.Static | BindingFlags.Public);

        public ToArrayFunctionInstance(Engine engine) : base(engine, null,null, false)
        {   
        }

        public override JsValue Call(JsValue thisObject, JsValue[] arguments)
        {
            var target = (thisObject.AsObject() is IObjectWrapper w)? w.Target : throw new NotSupportedException();
            var targetType = target.GetType();

            var enumImpl = targetType.GetInterfaces()
                .Where(_ => _.IsGenericType)
                .Where(_ => _.GetGenericTypeDefinition() == typeof(IEnumerable<>))
                .FirstOrDefault();

            if(enumImpl != null)
            {
                var arg = enumImpl.GetGenericArguments();
                var value = toArray.MakeGenericMethod(arg).Invoke(null, new[] { target });
                return JsValue.FromObject(Engine, value);
            }
            throw new NotSupportedException();
        }
    }
}
发布评论

评论列表(0)

  1. 暂无评论