String format a JSON string gives KeyError String format a JSON string gives KeyError json json

String format a JSON string gives KeyError


You need to double the outer braces; otherwise Python thinks { "File".. is a reference too:

output_format = '{{ "File": "{filename}", "Success": {success}, "ErrorMessage": "{error_msg}", "LogIdentifier": "{log_identifier}" }}'

Result:

>>> print output_format.format(filename='My_file_name',...                            success=True,...                            error_msg='',...                            log_identifier='123'){ "File": "My_file_name", "Success": True, "ErrorMessage": "", "LogIdentifier": "123" }

If, indicentally, you are producing JSON output, you'd be better off using the json module:

>>> import json>>> print json.dumps({'File': 'My_file_name',...                   'Success': True,...                   'ErrorMessage': '',...                   'LogIdentifier': '123'}){"LogIdentifier": "123", "ErrorMessage": "", "Success": true, "File": "My_file_name"}

Note the lowercase true in the output, as required by the JSON standard.


As mentioned by Tudor in a comment to another answer, the Template class was the solution that worked best for me. I'm dealing with nested dictionaries or list of dictionaries and handling those were not as straightforward.

Using Template though the solution is quite simple.

I start with a dictionary that is converted into a string. I then replace all instances of { with ${ which is the Template identifier to substitute a placeholder.

The key point of getting this to work is using the Template method safe_substitute. It will replace all valid placeholders like ${user_id} but ignore any invalid ones that are part of the dictionary structure, like ${'name': 'John', ....

After the substitution is done I remove any leftovers $ and convert the string back to a dictionary.

In the code bellow, resolve_placeholders returns a dictionary where each key matches a placeholder in the payload string and the value is substituted by the Template class.

from string import Template...payload = json.dumps(payload)payload = payload.replace('{', '${')replace_values = self.resolve_placeholders(payload)if replace_values:    string_template = Template(payload)    payload = string_template.safe_substitute(replace_values)payload = payload.replace('${', '{')payload = json.loads(payload)


To extend on Martijn Pieters answer and comment:

  1. According to MArtijn' comment, escaping the {..} pairs that are not placeholders is they way to go with nested dictionaries. I haven't succeded in doing that, so I suggest the following method.

  2. For nested dictionaries I tried doubling up on any { and } of the nested dictionaries.

a='{{"names":{{"a":"{name}"}}}}'

a.format(name=123) output:

output: '{"names":{"a":"123"}}'

But this makes using format to change values inside a json string, a over-complex method, so I use a twist on the format command.I replace ${param_name} in a json string. For example:

My predefined JSON looks like this:

my_json_dict = {     'parameter': [         {             'name': 'product',             'value': '${product}'         },         {             'name': 'suites',             'value': '${suites}'         },         {             'name': 'markers',             'value': '${markers}'         }     ] }

I provide this dictionary as values to replace instead of the parameters

parameters = {    'product': 'spam',    'suites': 'ham',    'markers': 'eggs'}

And use this code to do the replacment

json_str = json.dumps(my_json_dict)for parameter_name, parameter_value in parameters.iteritems():        parameter_name = '${'+parameter_name+'}'        json_str = json_str.replace(parameter_name, parameter_value)json_dict = json.loads(json_str)