简体   繁体   English

将Python对象转换为PyV8的JavaScript

[英]Converting Python objects to JavaScript for PyV8

I'm trying to pass Python data (lists, dicts, strings..., arbitrarily nested) to PyV8. 我正在尝试将Python数据(列表,字符串,字符串......,任意嵌套)传递给PyV8。

class Global(object):
    def __init__(self, data):
        self.data = data
ctx = PyV8.JSContext(Global([{'a':1}]))
ctx.enter()
res = ctx.eval('data.length')
js_len = PyV8.convert(res)
print js_len

The code above prints None, presumably because the data object is not transformed to a JSArray and thus data.length evaluates to undefined . 上面的代码打印None,大概是因为数据对象没有转换为JSArray,因此data.length计算为undefined Is there a reliable way to do the necessary conversion in PyV8 other than using JSON? 除了使用JSON之外,还有一种可靠的方法在PyV8中进行必要的转换吗?

Apparently PyV8 doesn't correctly convert python lists to Javascript arrays, which leads my_list.length to return undefined , which is getting converted to None. 显然PyV8没有正确地将python列表转换为Javascript数组,这导致my_list.length返回undefined ,后者将转换为None。

ctx = PyV8.JSContext()
ctx.enter()
ctx.locals.a = [{'a':1}]
print ctx.locals.a
#> [{'a': 1}]
print ctx.eval("a.length")
#> None
print ctx.eval("a[0].a")
#> 1
ctx.locals.blub = {'a':1}
print ctx.eval("blub.a")
#> 1
print ctx.eval("Object.keys(blub)")
#> a
ctx.locals.blub = {'a':[1,2,3]}
print ctx.eval("Object.keys(blub)")
#> a
print ctx.eval("blub.a")
#> [1, 2, 3]
ctx.locals.blub2 = [{'a':[1,2,3]}]
print ctx.eval("blub2")
#> [{'a': [1, 2, 3]}]
print ctx.eval("blub2.length")
#> None
print ctx.eval("Array.isArray(blub2)")
#> False
print ctx.eval("typeof(blub2)")
#> object
print ctx.eval("blub2[0].a")
#> [1, 2, 3]
print ctx.eval("typeof(blub.a)")
#> object
print ctx.eval("Array.isArray(blub.a)")
#> False

The answer is to use PyV8.JSArray(my_list) . 答案是使用PyV8.JSArray(my_list) I've written the following helper functions for my project that deal with various little problems and make it easy to convert back and forth between python and js objects. 我已经为我的项目编写了以下辅助函数来处理各种小问题,并且可以很容易地在python和js对象之间来回转换。 These are targeted at a specific version of PyV8 however (which is the only version I can recommend, see discussion in the linked issues) , so your results may vary if you use them as-is. 这些是针对PyV8的特定版本(这是我可以推荐的唯一版本,请参阅链接问题中的讨论) ,因此如果您按原样使用它们,结果可能会有所不同。 Example usage: 用法示例:

ctx.locals.blub3 = get_js_obj({'a':[1,2,3]})
ctx.locals.blub4 = get_js_obj([1,2,3])
ctx.eval("blub3.a.length")
#> 3
ctx.eval("blub4.length")
#> 3

And here are the functions. 以下是功能。

def access_with_js(ctx, route):
    if len(route) == 0:
        raise Exception("route must have at least one element")
    accessor_string = route[0]
    for elem in route[1:]:
        if type(elem) in [str, unicode]:
            accessor_string += "['" + elem + "']"
        elif type(elem) == int:
            accessor_string += "[" + str(elem) + "]"
        else:
            raise Exception("invalid element in route, must be text or number")
    return ctx.eval(accessor_string)

def get_py_obj(ctx, obj, route=[]):
    def dict_is_empty(dict):
        for key in dict:
            return False
        return True

    def access(obj, key):
        if key in obj:
            return obj[key]
        return None

    cloned = None
    if isinstance(obj, list) or isinstance(obj, PyV8.JSArray):
        cloned = []
        temp = str(access_with_js(ctx, route)) #working around a problem with PyV8 r429
        num_elements = len(obj)
        for index in range(num_elements):
            elem = obj[index]
            cloned.append(get_py_obj(ctx, elem, route + [index]))
    elif isinstance(obj, dict) or isinstance(obj, PyV8.JSObject):
        cloned = {}
        for key in obj.keys():
            cloned_val = None
            if type(key) == int:
                #workaround for a problem with PyV8 where it won't let me access
                #objects with integer accessors
                val = None
                try:
                    val = access(obj, str(key))
                except KeyError:
                    pass
                if val == None:
                    val = access(obj, key)
                cloned_val = get_py_obj(ctx, val, route + [key])
            else:
                cloned_val = get_py_obj(ctx, access(obj, key), route + [key])
            cloned[key] = cloned_val
    elif type(obj) == str:
        cloned = obj.decode('utf-8')
    else:
        cloned = obj
    return cloned

def get_js_obj(ctx,obj):
    #workaround for a problem with PyV8 where it will implicitely convert python lists to js objects
    #-> we need to explicitely do the conversion. see also the wrapper classes for JSContext above.
    if isinstance(obj, list):
        js_list = []
        for entry in obj:
            js_list.append(get_js_obj(ctx,entry))
        return PyV8.JSArray(js_list)
    elif isinstance(obj, dict):
        js_obj = ctx.eval("new Object();") # PyV8.JSObject cannot be instantiated from Python
        for key in obj.keys():

            try:
                js_obj[key] = get_js_obj(ctx,obj[key])
            except Exception, e:
                # unicode keys raise a Boost.Python.ArgumentError 
                # which can't be caught directly:
                # https://mail.python.org/pipermail/cplusplus-sig/2010-April/015470.html
                if (not str(e).startswith("Python argument types in")):
                    raise
                import unicodedata
                js_obj[unicodedata.normalize('NFKD', key).encode('ascii','ignore')] = get_js_obj(ctx,obj[key])
        return js_obj
    else:
        return obj

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM