Reputation: 1635
Say you have a template YAML file:
foo:
bar: {%VARIABLE%}
and want the value of bar
to be a stringified JSON? Ex:
foo:
bar: '{ "hey": "there" }'
In my case, the JSON is in a file, so I'm doing this:
VAR=$(cat my.json)
sed -i '' "s|{%VARIABLE%}|'$VAR'|g" foo.yaml
but if you leave the newlines in the JSON, sed complains.
If I use cat my.json | tr -d '\n'
to remove the newlines, the program ingesting my JSON says the private key in the JSON is invalid.
What's the proper way to do this?
Upvotes: 1
Views: 1216
Reputation: 76578
What you have there is not a YAML file, or even a template YAML file,
as that is not valid YAML: the (flow) mapping start indicator {
is not followed by a
key-value pair.
What you do have is a template for a YAML file, which is using the
jinja2 templating, and there is plug-in for ruamel.yaml
available to
update exactly that kind of templates (in fact usually a bit more
complex than yours). That plugin converts the template to a valid YAML
file, which can then be loaded, updated and dumped safely using Python.
This has the major advantage in that the value node can be set to be represented as a literal style scalar and that there is no problem with whatever JSON you throw at it, as your stringified JSON would at least need escaping of quotes (and possible of backslashes when using double quotes).
import sys
import ruamel.yaml
yaml_str = """\
foo:
bar: {%VARIABLE%}
"""
json_str = """\
{
"hey": "there's cake"
}
"""
yaml = ruamel.yaml.YAML(typ='jinja2')
data = yaml.load(yaml_str)
data['foo']['bar'] = ruamel.yaml.scalarstring.LiteralScalarString(json_str)
del data['foo'].ca._items['bar']
yaml.dump(data, sys.stdout)
which gives:
foo:
bar: |
{
"hey": "there's cake"
}
Normally the non-jinja2 part of the template is edited as YAML, in your case the del
is needed,
otherwise the %VARIABLE% would show up in a comment in the ouput.
I on purpose changed the value in the JSON to include a single quote. To include that in your example output you would need to do:
foo:
bar: '{ "hey": "there''s cake" }'
While this might still be doable using non-YAML aware tools like
sed
, taking care of multi-line JSON with empty lines properly is no
trivial. All these problems go away by inserting the element as a
literal style scalar, but this has to be done using a proper parser,
as that can e.g. take proper action when encountering a first line in
your JSON that is less indented than your second, etc.
Upvotes: 1
Reputation: 14723
I'd suggest using perl
instead, which is more robust than sed
, and allows writing some one-liners as well, such as:
VAR=$(<my.json)
perl -i.bak -wpe "s|{%VARIABLE%}|'$VAR'|g" foo.yaml
(tested with the file my.json
below)
{ "hey" : "there",
"status" : "ok" }
Finally, note that if the .json
file contains characters such as $ and @ that could have a special meaning in the replacement string, it would be worth it to rely on Perl's quoting operator q{}
:
perl -i.bak -wpe "my \$x=q{$VAR}; s|{%VARIABLE%}|'\$x'|g" foo.yaml
or alternatively:
perl -i.bak -wpe "my \$x='$VAR'; s|{%VARIABLE%}|'\$x'|g" foo.yaml
Upvotes: 1