14

JSONでエンコードされた非常に長いリストを含むオブジェクトを返すWebサービスを書いています。もちろん、データベースからオブジェクトをストリーミングできるように、Pythonリストではなくイテレーターを使用したいと思います。残念ながら、標準ライブラリ(json.JSONEncoder)のJSONエンコーダーは、JSONリストに変換されるリストとタプルのみを受け入れます(ただし、_iterencode_list実際には反復可能なもので機能するように見えます)。

docstringは、オブジェクトをリストに変換するためにデフォルトをオーバーライドすることを提案していますが、これはストリーミングの利点を失うことを意味します。以前は、プライベートメソッドを無効にしましたが、(予想どおり)エンコーダーがリファクタリングされたときに壊れました。

ストリーミング方式でPythonのJSONリストとしてイテレータをシリアル化するための最良の方法は何ですか?

4

4 に答える 4

2

これをモジュール ファイルに保存してインポートするか、コードに直接貼り付けます。

'''
Copied from Python 2.7.8 json.encoder lib, diff follows:
@@ -331,6 +331,8 @@
                     chunks = _iterencode(value, _current_indent_level)
                 for chunk in chunks:
                     yield chunk
+        if first:
+            yield buf
         if newline_indent is not None:
             _current_indent_level -= 1
             yield '\n' + (' ' * (_indent * _current_indent_level))
@@ -427,12 +429,12 @@
             yield str(o)
         elif isinstance(o, float):
             yield _floatstr(o)
-        elif isinstance(o, (list, tuple)):
-            for chunk in _iterencode_list(o, _current_indent_level):
-                yield chunk
         elif isinstance(o, dict):
             for chunk in _iterencode_dict(o, _current_indent_level):
                 yield chunk
+        elif hasattr(o, '__iter__'):
+            for chunk in _iterencode_list(o, _current_indent_level):
+                yield chunk
         else:
             if markers is not None:
                 markerid = id(o)
'''
from json import encoder

def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
        _key_separator, _item_separator, _sort_keys, _skipkeys, _one_shot,
        ## HACK: hand-optimized bytecode; turn globals into locals
        ValueError=ValueError,
        basestring=basestring,
        dict=dict,
        float=float,
        id=id,
        int=int,
        isinstance=isinstance,
        list=list,
        long=long,
        str=str,
        tuple=tuple,
    ):

    def _iterencode_list(lst, _current_indent_level):
        if not lst:
            yield '[]'
            return
        if markers is not None:
            markerid = id(lst)
            if markerid in markers:
                raise ValueError("Circular reference detected")
            markers[markerid] = lst
        buf = '['
        if _indent is not None:
            _current_indent_level += 1
            newline_indent = '\n' + (' ' * (_indent * _current_indent_level))
            separator = _item_separator + newline_indent
            buf += newline_indent
        else:
            newline_indent = None
            separator = _item_separator
        first = True
        for value in lst:
            if first:
                first = False
            else:
                buf = separator
            if isinstance(value, basestring):
                yield buf + _encoder(value)
            elif value is None:
                yield buf + 'null'
            elif value is True:
                yield buf + 'true'
            elif value is False:
                yield buf + 'false'
            elif isinstance(value, (int, long)):
                yield buf + str(value)
            elif isinstance(value, float):
                yield buf + _floatstr(value)
            else:
                yield buf
                if isinstance(value, (list, tuple)):
                    chunks = _iterencode_list(value, _current_indent_level)
                elif isinstance(value, dict):
                    chunks = _iterencode_dict(value, _current_indent_level)
                else:
                    chunks = _iterencode(value, _current_indent_level)
                for chunk in chunks:
                    yield chunk
        if first:
            yield buf
        if newline_indent is not None:
            _current_indent_level -= 1
            yield '\n' + (' ' * (_indent * _current_indent_level))
        yield ']'
        if markers is not None:
            del markers[markerid]

    def _iterencode_dict(dct, _current_indent_level):
        if not dct:
            yield '{}'
            return
        if markers is not None:
            markerid = id(dct)
            if markerid in markers:
                raise ValueError("Circular reference detected")
            markers[markerid] = dct
        yield '{'
        if _indent is not None:
            _current_indent_level += 1
            newline_indent = '\n' + (' ' * (_indent * _current_indent_level))
            item_separator = _item_separator + newline_indent
            yield newline_indent
        else:
            newline_indent = None
            item_separator = _item_separator
        first = True
        if _sort_keys:
            items = sorted(dct.items(), key=lambda kv: kv[0])
        else:
            items = dct.iteritems()
        for key, value in items:
            if isinstance(key, basestring):
                pass
            # JavaScript is weakly typed for these, so it makes sense to
            # also allow them.  Many encoders seem to do something like this.
            elif isinstance(key, float):
                key = _floatstr(key)
            elif key is True:
                key = 'true'
            elif key is False:
                key = 'false'
            elif key is None:
                key = 'null'
            elif isinstance(key, (int, long)):
                key = str(key)
            elif _skipkeys:
                continue
            else:
                raise TypeError("key " + repr(key) + " is not a string")
            if first:
                first = False
            else:
                yield item_separator
            yield _encoder(key)
            yield _key_separator
            if isinstance(value, basestring):
                yield _encoder(value)
            elif value is None:
                yield 'null'
            elif value is True:
                yield 'true'
            elif value is False:
                yield 'false'
            elif isinstance(value, (int, long)):
                yield str(value)
            elif isinstance(value, float):
                yield _floatstr(value)
            else:
                if isinstance(value, (list, tuple)):
                    chunks = _iterencode_list(value, _current_indent_level)
                elif isinstance(value, dict):
                    chunks = _iterencode_dict(value, _current_indent_level)
                else:
                    chunks = _iterencode(value, _current_indent_level)
                for chunk in chunks:
                    yield chunk
        if newline_indent is not None:
            _current_indent_level -= 1
            yield '\n' + (' ' * (_indent * _current_indent_level))
        yield '}'
        if markers is not None:
            del markers[markerid]

    def _iterencode(o, _current_indent_level):
        if isinstance(o, basestring):
            yield _encoder(o)
        elif o is None:
            yield 'null'
        elif o is True:
            yield 'true'
        elif o is False:
            yield 'false'
        elif isinstance(o, (int, long)):
            yield str(o)
        elif isinstance(o, float):
            yield _floatstr(o)
        elif isinstance(o, dict):
            for chunk in _iterencode_dict(o, _current_indent_level):
                yield chunk
        elif hasattr(o, '__iter__'):
            for chunk in _iterencode_list(o, _current_indent_level):
                yield chunk
        else:
            if markers is not None:
                markerid = id(o)
                if markerid in markers:
                    raise ValueError("Circular reference detected")
                markers[markerid] = o
            o = _default(o)
            for chunk in _iterencode(o, _current_indent_level):
                yield chunk
            if markers is not None:
                del markers[markerid]

    return _iterencode

encoder._make_iterencode = _make_iterencode
于 2014-09-29T07:25:38.623 に答える
-1

実際のストリーミングは では十分にサポートされていませんjson。これは、クライアント アプリケーションもストリーミングをサポートする必要があることを意味するためです。ストリーミングされたストリームの読み取りをサポートする Java ライブラリがいくつかありますがjson、あまり汎用的ではありません。yailストリーミングをサポートする C ライブラリである のPython バインディングもいくつかあります。

Yamlの代わりに使えるかもしれませんjsonYamljson のスーパーセットです。両側でのストリーミングのサポートが強化されており、jsonメッセージは引き続き有効ですyaml

しかし、あなたの場合、オブジェクト ストリームを個別のjsonメッセージのストリームに分割する方がはるかに簡単な場合があります。

こちらのディスカッションも参照してください。どのクライアント ライブラリがストリーミングをサポートしているか: JSON 用のストリーミング API はありますか?

于 2012-10-01T09:50:07.290 に答える
-2

それほど単純ではありません。WSGI (ほとんどの人が使用しているもの) プロトコルはストリーミングをサポートしていません。そして、それをサポートしているサーバーは仕様に違反しています。

また、準拠していないサーバーを使用する場合でも、ijsonなどを使用する必要があります。また、あなたと同じ問題を抱えていたこの男を見てください http://www.enricozini.org/2011/tips/python-stream-json/

編集:それから、すべてはクライアントに帰着します。これは、Javascript(?) で記述されると思います。しかし、不完全な JSON チャンクから javascript (または任意の言語) オブジェクトを構築する方法がわかりません。私が考えることができる唯一のことは、長いJSONを(サーバー側で)小さなJSONオブジェクトに手動で分解し、それを1つずつクライアントにストリーミングすることです。しかし、これはステートレスな http リクエスト/レスポンスではなく、websocket を必要とします。また、Web サービスで REST API を意味する場合、それはあなたが望むものではないと思います。

于 2012-10-01T09:47:16.353 に答える