我正在尝试将一部分 Python 2.6 代码升级到 Python 2.7。此代码使用 json 模块生成一些 JavaScript(不兼容 JSON),然后将其插入到脚本的其余部分中。

总体思路是能够插入代码或引用在别处定义的变量:它不打算用作 JSON 数据,而是用作 JavaScript 代码。

这是适用于 Python 2.6 的自定义编码器:

import json

class RawJavaScriptText:
    def __init__(self, jstext):
        self._jstext = jstext
    def get_jstext(self):
        return self._jstext

class RawJsJSONEncoder(json.JSONEncoder):
    def _iterencode_default(self, o, markers=None):
        if isinstance(o, RawJavaScriptText):
            yield self.default(o)
        else:
            json.JSONEncoder._iterencode_default(self, o, markers)

    def default(self, o):
        if isinstance(o, RawJavaScriptText):
            return o.get_jstext()
        else:
            return json.JSONEncoder.default(self, o)

testvar = {
   'a': 1,
   'b': 'abc',
   # RawJavaScriptText will be inserted as such, no serialisation.
   'c': RawJavaScriptText('function() { return "Hello World"; }'),
   'd': RawJavaScriptText('some_variable_name')
}

print json.dumps(testvar, cls=RawJsJSONEncoder)

使用 Python 2.6,我们得到了所需的结果:

{ "a": 1, "c": function() { return "Hello World"; },
  "b": "abc", "d": some_variable_name }

使用 Python 2.7,一切都变成了字符串,从而失去了 JavaScript 代码的有效性:

{ "a": 1, "c": "function() { return \"Hello World\"; }",
  "b": "abc", "d": "some_variable_name" }

(作为旁注,这仅与一组预定义的原始 JavaScript 值一起使用,以防止潜在的注入(inject)或误用。)

当然,这是因为 _iterencode_defaultJSONEncoder 方法在 Python 2.7 版本的 json 模块中不存在。诚然,它并不意味着首先要被覆盖。

在 Python 2.7 中还有另一种方法可以实现这个目标吗?使用 JSON 库的基础能够以这种方式生成 JavaScript 代码是相当方便的。

编辑: 这是完整的工作解决方案,按照 James Henstridge 的建议使用替换。我使用随机 UUID 作为替换 token ,这应该可以防止任何冲突。这样,这是使用 Python 2.6 和 2.7 的直接替代品。

import json
import uuid

class RawJavaScriptText:
    def __init__(self, jstext):
        self._jstext = jstext
    def get_jstext(self):
        return self._jstext

class RawJsJSONEncoder(json.JSONEncoder):
    def __init__(self, *args, **kwargs):
        json.JSONEncoder.__init__(self, *args, **kwargs)
        self._replacement_map = {}

    def default(self, o):
        if isinstance(o, RawJavaScriptText):
            key = uuid.uuid4().hex
            self._replacement_map[key] = o.get_jstext()
            return key
        else:
            return json.JSONEncoder.default(self, o)

    def encode(self, o):
        result = json.JSONEncoder.encode(self, o)
        for k, v in self._replacement_map.iteritems():
             result = result.replace('"%s"' % (k,), v)
        return result

testvar = {
   'a': 1,
   'b': 'abc',
   'c': RawJavaScriptText('function() { return "Hello World"; }'),
   'd': [ RawJavaScriptText('some_variable_name') ],
   'e': {
       'x': RawJavaScriptText('some_variable_name'),
       'y': 'y'
   }
}

print json.dumps(testvar, cls=RawJsJSONEncoder)

结果(2.6 和 2.7):
{"a": 1, "c": function() { return "Hello World"; },
 "b": "abc",
 "e": {"y": "y", "x": some_variable_name},
 "d": [some_variable_name]}

最佳答案

当在幕后使用的 C 扩展扩展到涵盖更多编码过程时,您使用的未记录的私有(private)接口(interface)似乎已经消失。

一种替代方法是为您的 RawJavaScriptText 值插入占位符字符串,并对 dumps 的输出进行后处理以将这些占位符转换为您需要的形式。

例如:

>>> data = {'foo': '@@x@@'}
>>> print json.dumps(data)
{"foo": "@@x@@"}
>>> print json.dumps(data).replace('"@@x@@"', 'some_variable_name')
{"foo": some_variable_name}

如果您的 JSON 包含不受信任的数据,您将需要小心这种技术:您不希望处于局外人可以意外地将此类占位符添加到输出的位置。

关于python - Python 2.7 中的自定义 JSON 编码器,用于插入纯 JavaScript 代码,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/13188719/

10-13 06:44
查看更多