从 dict 解码为 str 时无法格式化 Python 字符串

Unable to format Python string while decoding from dict to str

我有一个字典,我将其编码为这样的字符串:

import json

template = json.dumps({
    '_index': '{0}',
    '_type': '{1}',
    '_id': '{2}',
    '_source': {
        'doc': '{3}',
        'doc_as_upsert': True
    }
})

现在,我尝试按照此处提到的新 python 约定对其进行格式化:https://pyformat.info/

print template.format('one','two','three','four')

但是,我得到一个错误

Traceback (most recent call last): File "python", line 1, in KeyError: '"_type"'

我做错了什么?

问题源于您 JSON 中的花括号 - 您需要对它们进行两次转义才能使 str.format() 正常工作,例如:

import json

template = json.dumps({
    '_index': '{0}',
    '_type': '{1}',
    '_id': '{2}',
    '_source': {
        'doc': '{3}',
        'doc_as_upsert': True
    }
})

template = template.replace("{", "{{").replace("}", "}}")

print(template.format('one','two','three','four'))

它不会再出错,但它也会转义你的参数花括号,所以它们不会被 str.format() 取代,所以你也必须发明你自己的 'parameter' 转义(确保它不会显示为 JSON 的标记代码,但是,就像花括号一样),例如使用 <> 代替:

import json

template = json.dumps({
    '_index': '<0>',
    '_type': '<1>',
    '_id': '<2>',
    '_source': {
        'doc': '<3>',
        'doc_as_upsert': True
    }
})

template = template.replace("{", "{{").replace("}", "}}").replace("<", "{").replace(">", "}")

print(template.format('one', 'two', 'three', 'four'))

但最好在将数据转换为 JSON 之前直接替换您的数据。您可以在 dict 中对每个 (str) value 单独调用 str.format(),将带有所有参数的 dict 传递给它并使用命名参数(即{one}) 从扩展键中获取所需的参数。

UPDATE:您甚至不需要递归最后一个数据,因为 json 序列化器无论如何都会递归它,但不幸的是json 模块不能轻松地交换字符串序列化的默认行为,因此您必须进行一些猴子修补:

from json import dumps, encoder

def template_json(data, args, **kwargs):
    json_s1, json_s2 = encoder.encode_basestring, encoder.encode_basestring_ascii
    encoder.encode_basestring = lambda s: json_s1(s.format(**args))
    encoder.encode_basestring_ascii = lambda s: json_s2(s.format(**args))
    try:
        return dumps(data, **kwargs)
    finally:
        encoder.encode_basestring, encoder.encode_basestring_ascii = json_s1, json_s2

它本质上是暂时将内部 JSON 字符串构建方法与首先应用格式的方法包装起来,然后还原所有内容,以便其他可能依赖于 json 模块的函数无法获取意外行为(尽管这里也有一点危险——这不是线程安全的)。因为它将一个一个地读取元素,我们不能真正使用位置格式,所以这使用上面建议的命名格式。您可以将其测试为:

data = {
    '_index': '{one}',
    '_type': '{two}',
    '_id': '{three}',
    '_source': {
        'doc': '{four}',
        'doc_as_upsert': True,
    }
}

template_vars = {"one": "value 1", "two": "value 2", "three": "value 3", "four": "value 4"}

print(template_json(data, template_vars, indent=4))

导致:

{
    "_source": {
        "doc": "value 4",
        "doc_as_upsert": true
    },
    "_index": "value 1",
    "_id": "value 3",
    "_type": "value 2"
}

但是,一般来说,如果您必须 修改 您的系统以实现您想要的效果 - 您可能首先要重新考虑这是否是正确的方法,您的方法是否可行? objective有没有更简单的方式实现?