我正在尝试将部分Python 2.6代码升级到Python 2.7。这段代码使用该json模块生成一些JavaScript(不兼容JSON),然后将其插入脚本的其余部分。
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值一起使用,以防止可能的注入或滥用。)
当然,这样做的原因是的_iterencode_default方法JSONEncoder在json模块的Python 2.7版本中不存在。诚然,它并不是要一开始就被覆盖。
_iterencode_default
JSONEncoder
在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)
结果(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扩展范围以涵盖更多编码过程时,您正在使用的未记录私有接口似乎消失了。
一种替代方法是为您的RawJavaScriptText值插入占位符字符串,并对输出进行后期处理,dumps以将这些占位符转换为所需的表单。
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包含不受信任的数据,那么您将需要小心这种技术:您不希望局外人意外地将此类占位符添加到输出中。