Bru*_*uno 4 python json python-2.6 python-2.7
我正在尝试将一部分 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)
Run Code Online (Sandbox Code Playgroud)
使用Python 2.6,我们得到所需的结果:
{ "a": 1, "c": function() { return "Hello World"; },
"b": "abc", "d": some_variable_name }
Run Code Online (Sandbox Code Playgroud)
使用Python 2.7,所有内容都变成字符串,从而失去JavaScript代码的有效性:
{ "a": 1, "c": "function() { return \"Hello World\"; }",
"b": "abc", "d": "some_variable_name" }
Run Code Online (Sandbox Code Playgroud)
(顺便说一句,这仅与一组预定义的原始 JavaScript 值一起使用,以防止潜在的注入或误用。)
当然,这是因为Python 2.7版本的模块中不存在_iterencode_default
方法。诚然,它一开始就不应该被覆盖。JSONEncoder
json
在Python 2.7中还有另一种方法可以实现这个目标吗?使用 JSON 库的基础能够以这种方式生成 JavaScript 代码相当方便。
编辑:这是完整的工作解决方案,使用 James Henstridge 建议的替换。我使用随机 UUID 作为替换令牌,这应该可以防止任何冲突。这样,就可以直接替代 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)
Run Code Online (Sandbox Code Playgroud)
结果(2.6 和 2.7):
{"a": 1, "c": function() { return "Hello World"; },
"b": "abc",
"e": {"y": "y", "x": some_variable_name},
"d": [some_variable_name]}
Run Code Online (Sandbox Code Playgroud)
当幕后使用的 C 扩展扩展以涵盖更多编码过程时,您使用的未记录的私有接口似乎已经消失。
一种替代方法是插入RawJavaScriptText
值的占位符字符串,并对输出进行后处理,dumps
将这些占位符转换为您需要的形式。
例如:
>>> data = {'foo': '@@x@@'}
>>> print json.dumps(data)
{"foo": "@@x@@"}
>>> print json.dumps(data).replace('"@@x@@"', 'some_variable_name')
{"foo": some_variable_name}
Run Code Online (Sandbox Code Playgroud)
如果您的 JSON 包含不受信任的数据,您需要小心这种技术:您不希望外部人员意外地将此类占位符添加到输出中。