python response.json保留顺序

时间:2018-07-18 23:16:51

标签: python json python-requests

我有一个json对象,该对象是针对帖子调用返回的

typealias closure = (UIImageView?) -> Void

func imageFromUrl(musicImageUrlString:String, completion: @escaping closure) {
    let image = UIImage()
    let imageView = UIImageView(image: image)
    imageView.sd_setImage(with: URL(string: musicImageUrlString), placeholderImage: UIImage(named: "App-Default"),options:SDWebImageOptions(rawValue: 0), completed: { (image, error, cacheType, imageURL) in
        imageView.image = image
        completion(imageView)
    })
}

我正在做r = requests.post("url", json=data) 以获取json对象。但是据我了解,它会创建一个无序的dict对象。我需要保留订单。

我看到了此处描述的解决方案:Items in JSON object are out of order using "json.dumps"?

但是我的挑战是我的出发点是一个响应对象。如何将其转换为保留顺序的json?

添加更多详细信息:

我的API调用返回以下形式的对象:

r.json()

我有一个包含三列的表,分别为key01,key02和keyN。

我需要在对key01,key02和keyN的特定顺序进行一些小的操作后,将这个json对象发布到该软件上。

但是,一旦我执行response.json(),它就会改变顺序。我尝试使用其他两个线程中提到的orderedlist方法,但到目前为止,我的对象看起来像这样:

[{
    "key01": "value01",
    "key02": "value02",
    "keyN": "valueN"
},
{
    "key01": "value01",
    "key02": "value02",
    "keyN": "valueN"
},
{
    "key01": "value01",
    "key02": "value02",
    "keyN": "valueN"
}
]

我如何获取看起来像这样的json:b"OrderedDict([('key01','value01'),('key02','value02'),('keyN','valueN')])

2 个答案:

答案 0 :(得分:1)

这些请求没有最好的文档,但是通过阅读其源代码on the .json() method,我们可以看到它的定义如下:

def json(self, **kwargs):
    r"""Returns the json-encoded content of a response, if any.
    :param \*\*kwargs: Optional arguments that ``json.loads`` takes.
    :raises ValueError: If the response body does not contain valid json.
    """

    if not self.encoding and self.content and len(self.content) > 3:
        # No encoding set. JSON RFC 4627 section 3 states we should expect
        # UTF-8, -16 or -32. Detect which one to use; If the detection or
        # decoding fails, fall back to `self.text` (using chardet to make
        # a best guess).
        encoding = guess_json_utf(self.content)
        if encoding is not None:
            try:
                return complexjson.loads(
                    self.content.decode(encoding), **kwargs
                )
            except UnicodeDecodeError:
                # Wrong UTF codec detected; usually because it's not UTF-8
                # but some other 8-bit codec.  This is an RFC violation,
                # and the server didn't bother to tell us what codec *was*
                # used.
                pass
    return complexjson.loads(self.text, **kwargs)

其中complexjson是标准json库,如果安装了simplejson,则为.json()

知道了这一点,您实际上可以将关键字参数传递给json.loads(),该参数将直接转到from collections import OrderedDict r.json(object_pairs_hook=OrderedDict) 。这意味着您可以按照answer you linked的建议进行操作:


object_pairs_hook

  

json.loads()

     

object_pairs_hook 是一个可选功能,   将被调用的任何对象文字的结果调用   对的有序列表。 dict的返回值为   代替object_hook。此功能可用于实现自定义   解码器。如果还定义了object_pairs_hook,则object_pairs_hook将采用   优先。


  

simplejson.loads()

     

object_pairs_hook 是一个可选函数,将与   具有对的有序列表的任何对象文字解码的结果。的   返回值dict将代替key。   此功能可用于实现依赖于   将valuecollections.OrderedDict对解码的顺序(例如,   object_hook将记住插入顺序)。如果   还定义了object_pairs_hookobject_pairs_hook优先。

因此,无论哪种方式,您都可以为r.json()提供text = r.content.decode(requests.utils.guess_json_utf(r.content)).encode('utf-8') 关键字参数。


从我收到的评论信息中,您甚至不需要解析json,只需执行以下操作:

{{1}}

您可以将文本“发布”到所需的任何地方。

答案 1 :(得分:1)

依靠服务器(特别是您无法控制的json密钥)的顺序非常脆弱。 RFC says

  

对象是零个或多个名称/值对的无序集合,其中名称是字符串,值是字符串,数字,布尔值,null,对象或数组。

它也特别评论:

  观察到

JSON解析库在是否使调用软件可见对象成员的顺序方面有所不同。行为不取决于成员顺序的实现将可以互操作,因为它们不会受到这些差异的影响。

因此,更改服务器订购生成的JSON的方式(在服务器上)符合RFC。

如果您不知道服务器是否正在使用可保证订单的序列化库,则将来可能会中断(如果库发生更改)。即使您这样做,即使该库采用的服务器语言等效于dict,升级语言或标准库也可能会更改该dict的语义,从而顺序会更改(并且代码会中断)。例如,从Python 3.6到3.7 dicts从任意顺序更改为插入顺序。在其他语言中,例如rust,它们将其哈希图使用的哈希函数播种到prevent DoS attacks中,其顺序可能取决于用来播种这些哈希函数的随机性(在运行时确定,并且可能不同如果您说重启服务器)。

如果您知道自己需要按某种顺序来构造数据的方式,那就安全得多:

from collections import OrderedDict

ORDERED_KEYS = ['first', 'second', 'third']
ordered_json = OrderedDict((k, r.json()[k]) for k in ORDERED_KEYS)

从您的评论看来,您似乎需要再次序列化该词典。如果您在json.dumps上使用OrderedDict,则序列化将按插入顺序进行:

import json

serialized_ordered_json = json.dumps(ordered_json)