Search code examples
pythonjsonpython-3.xstring-formatting

How to use str.format inside a string of json format?


Python Version 3.5

I'm trying to make an API call to configure a device using json as the format. Some of the json will vary depending on the desired naming, so I need to call a variable in the string. I am able to accomplish this using the old style %s... % (variable), but not with the new style {}... .format(variable).

Failed EX:

(Testing with {"fvAp":{"attributes":{"name":(variable)}}})

a = "\"app-name\""

app_config = ''' { "fvAp": { "attributes": { "name": {} }, "children": [ { "fvAEPg": { "attributes": { "name": "app" }, "children": [ { "fvRsBd": { "attributes": { "tnFvBDName": "default" }, } } ] } }, { "fvAEPg": { "attributes": { "name": "db" }, "children": [ { "fvRsBd": { "attributes": { "tnFvBDName": "default" }, } } ] } } ] } } '''.format(a)

print(app_config)

Traceback (most recent call last): File "C:/..., line 49, in '''.format('a') KeyError: '\n "fvAp"'

Working EX:

a = "\"app-name\""

app_config = ''' { "fvAp": { "attributes": { "name": %s }, "children": [ { "fvAEPg": { "attributes": { "name": "app" }, "children": [ { "fvRsBd": { "attributes": { "tnFvBDName": "default" }, } } ] } }, { "fvAEPg": { "attributes": { "name": "db" }, "children": [ { "fvRsBd": { "attributes": { "tnFvBDName": "default" }, } } ] } } ] } } ''' % a

print(app_config)

How do I get this to work using str.format method?


Solution

  • Format String Syntax section says:

    Format strings contain “replacement fields” surrounded by curly braces {}. Anything that is not contained in braces is considered literal text, which is copied unchanged to the output. If you need to include a brace character in the literal text, it can be escaped by doubling: {{ and }}.

    So if you want to use .format method, you need to escape all JSON curly braces in your template string:

    >>> '{{"fvAp": {{"attributes": {{"name": {}}}}}}}'.format('"app-name"')
    '{"fvAp": {"attributes": {"name": "app-name"}}}'
    

    That looks really bad.

    There's a better way to do that with string.Template:

    >>> from string import Template
    >>> t = Template('{"fvAp": {"attributes": {"name": "${name}"}}')
    >>> t.substitute(name='StackOverflow')
    '{"fvAp": {"attributes": {"name": "StackOverflow"}}'
    

    Though I suggest abandoning the idea of generating configs this way altogether and using a factory function and json.dumps instead:

    >>> import json
    >>> def make_config(name):
    ...     return {'fvAp': {'attributes': {'name': name}}}
    >>> app_config = make_config('StackOverflow')
    >>> json.dumps(app_config)
    '{"fvAp": {"attributes": {"name": "StackOverflow"}}}'