Filipe Ferminiano
Filipe Ferminiano

Reputation: 8801

JSON string with format() keyerror

I'm trying to use .format() to concatenate some variables in a json string

subscription_info = '{"endpoint":"xxx","keys":{"p256dh":"xxx","auth":"xxx"}}'

send_data = '{"message":"message_test", "subscription_info": {$subscription_info} }'.format(subscription_info=subscription_info)

But I'm getting

KeyError: '"message"'

How can I fix this?

Upvotes: 0

Views: 403

Answers (2)

freakish
freakish

Reputation: 56547

You are breaking format rules. Normally when you use format Python looks for the following

"{" [field_name] ["!" conversion] [":" format_spec] "}"

So in your case "message" becomes a field_name, since { is before it and : after it. This obviously doesn't work. The .format() method is not meant to be used with complex, nested text structures.

One way is to use string.Template together with .substitute instead:

>>> tmpl = string.Template('{"message":"message_test", "subscription_info": {$subscription_info} }')
>>> tmpl.substitute(subscription_info=subscription_info)
'{"message":"message_test", "subscription_info": {{"endpoint":"xxx","keys":{"p256dh":"xxx","auth":"xxx"}}} }'

However it also isn't meant to be used with nested text structures.

Since you are using JSON then perhaps you should parse and serialize the JSON instead. This is the proper way to handle the problem:

>>> import json
>>> subscription_info = {"endpoint":"xxx","keys":{"p256dh":"xxx","auth":"xxx"}}
>>> send_data = {"message":"message_test", "subscription_info": subscription_info }
>>> json.dumps(send_data)
'{"subscription_info": {"endpoint": "xxx", "keys": {"auth": "xxx", "p256dh": "xxx"}}, "message": "message_test"}'

It is the cleanest and safest method.

Upvotes: 1

Moses Koledoye
Moses Koledoye

Reputation: 78564

Double up the outer curly braces to avoid interpreting message as a format field:

send_data = '{{"message":"message_test", "subscription_info": {subscription_info} }}'.format(subscription_info=subscription_info)
print(send_data)
# {"message":"message_test", "subscription_info": {"endpoint":"xxx","keys":{"p256dh":"xxx","auth":"xxx"}} }

And you don't need the $ in your format string; a valid python indentifier is required if you must use a named field.

Upvotes: 1

Related Questions