4

Python2.6コードの一部をPython2.7にアップグレードしようとしています。このコードは、jsonモジュールを使用してJavaScript(JSON準拠ではない)を生成し、それをスクリプトの残りの部分に挿入します。

一般的な考え方は、コードを挿入したり、他の場所で定義されている変数を参照したりできるようにすることです。これは、JSONデータとして使用するためのものではなく、JavaScriptコードとして使用するためのものです。

Python2.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値のセットでのみ使用されます。)

もちろん、この理由は、Python2.7バージョンのモジュールにはの_iterencode_defaultメソッドが存在しないためです。確かに、そもそもオーバーライドすることを意図したものではありません。JSONEncoderjson

Python 2.7でこの目標を達成する別の方法はありますか?JSONライブラリの基盤を使用して、この方法でJavaScriptコードを生成できるようにするのはかなり便利です。

編集:これは、James Henstridgeによって提案された置換を使用した、完全に機能するソリューションです。置換トークンにランダムなUUIDを使用しています。これにより、競合を防ぐことができます。このように、これはPython2.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]}
4

1 に答える 1

5

カバーの下で使用されていたC拡張機能が、より多くのエンコードプロセスをカバーするように拡張されたときに、使用していたドキュメント化されていないプライベートインターフェイスがなくなったようです。

1つの代替方法は、値のプレースホルダー文字列を挿入し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に信頼できないデータが含まれている場合は、この種の手法に注意する必要があります。部外者がそのようなプレースホルダーを予期せず出力に追加できるような状況になりたくない場合です。

于 2012-11-02T04:43:49.250 に答える