【问题标题】:python response.json preserve orderpython response.json 保留顺序
【发布时间】:2018-07-19 08:28:07
【问题描述】:

我有一个 json 对象,它是针对 post 调用返回的

r = requests.post("url", json=data)

我正在做r.json() 来获取 json 对象。但据我了解,它创建了一个无序的 dict 对象。我需要保留订单。

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

但我的挑战是我的起点是响应对象。如何获取并转换为保留顺序的 json?

添加更多细节:

我的 API 调用返回一个表单对象:

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

我有一个包含三列 key01、key02 和 keyN 的表。

在对维护 key01、key02 和 keyN 的特定顺序的软件进行一些小操作后,我需要将此 json 对象发布。

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

b"OrderedDict([('key01','value01'),('key02','value02'),('keyN','valueN')])

我如何获得一个看起来像这样的 json:{"key01":"value01","key02":"value02","keyN":"valueN"}

【问题讨论】:

  • 特别是在 data 设为 OrderedDict
  • @BaileyParker OP 要求订购返回 json(由r.json() 访问),他们没有询问data
  • 确实,我看错了。我认为依赖来自服务器的 json 密钥的顺序(尤其是您无法控制的,但即使是您可以控制的)非常脆弱。我建议反对它。如果您想以特定顺序遍历dict,为什么不列出键keys = ['first', 'second', ...] 然后for k in key: r.json()[k]
  • 嗨@BaileyParker 感谢您的回复。我有一个内部进程,它接受一个 json 对象并将其映射到数据库表。中的行顺序。表必须与 json 中的确切序列匹配。不幸的是,我无法更改中间逻辑(它是供应商产品)。从 API 返回的元素序列与表中相同。 response.json() 方法正在改变它。

标签: python json python-requests


【解决方案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 是标准库jsonsimplejson(如果已安装)。

知道了这一点,您实际上可以将关键字参数传递给.json(),这将直接转到json.loads()。这意味着您可以执行answer you linked 建议的操作:


from collections import OrderedDict
r.json(object_pairs_hook=OrderedDict)

json.loads()

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


simplejson.loads()

object_pairs_hook 是一个可选函数,将使用 使用有序对列表的任何对象文字解码的结果。这 将使用object_pairs_hook 的返回值而不是dict。 此功能可用于实现依赖于 为了解码 keyvalue 对(例如, collections.OrderedDict 会记住插入的顺序)。如果 object_hook 也被定义,object_pairs_hook 优先。

因此,无论哪种方式,您都可以将 object_pairs_hook 关键字参数提供给 r.json()


从我从 cmets 收到的信息来看,你甚至不需要解析 json,只需:

text = r.content.decode(requests.utils.guess_json_utf(r.content)).encode('utf-8')

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

【讨论】:

  • 嗨@abccd,感谢您的详细描述。非常有帮助。我在问题中添加了更多细节。这对解决我的问题有帮助吗?
  • 仍然...r.json(object_pairs_hook=OrderedDict) 不起作用吗?它返回的对象基本上是您想要的dict 的有序版本。对于 Pythondict 未排序。
  • 不 :(。它正在将我的 json 转换为如下所示:b"OrderedDict([('key01','value01'),('key02','value02'),('keyN ','valueN')]) WHEREAS 我需要它看起来像这样:{"key01":"value01","key02":"value02","keyN":"valueN"}。这是我原来的格式顺便说一句,在响应对象中。
  • @Anubis05 从阅读你的其他评论,我想你想要text = r.content.decode(requests.utils.guess_json_utf(r.content)).encode('utf-8')
  • 顺便说一句,我使用的是 python 3.5.2
【解决方案2】:

依赖来自服务器(尤其是您无法控制的)的 json 密钥的顺序是非常脆弱的。 RFC says

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

它还专门cmets:

观察到 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)

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

import json

serialized_ordered_json = json.dumps(ordered_json)

【讨论】:

  • 我刚试过这种方法。这就是你所描述的。但是现在我的 json 对象看起来像这样: b"OrderedDict([('key01','value01'),('key02','value02'),('keyN','valueN')]) 。但是必须看起来像这样:{"key01":"value01","key02":"value02","keyN":"valueN"}。这是下一个进程接受的唯一格式。我得到的初始 json 格式与预计在接下来的步骤中。
  • 或者换个方式来形容。理想情况下,我希望 responseObj ->json ->utf-8 encoding ->post the json 。最后一步的 json 需要看起来像这样 {"key01":"value01","key02":"value02","keyN":"valueN"} 这是我在第二步中得到的。但是由于我正在执行 response.json() 以到达第 2 步,所以它打乱了顺序。我不能直接从响应对象中获取一个 json 对象,应用 utf-8 并传递到下一步吗?
  • 查看我的编辑以了解如何以序列化形式取回。
猜你喜欢
  • 2011-05-21
  • 1970-01-01
  • 1970-01-01
  • 2022-08-08
  • 2013-03-17
  • 2015-12-08
  • 1970-01-01
  • 2017-06-18
  • 2012-05-31
相关资源
最近更新 更多