Reputation: 21
ruamel.yaml
docs are sparse, and I haven't found the responses I needed here.
Contents of my_file.yaml
:
desc: "blahblahblah"
Q1: How do I (programmatically) add "size" as an element at the same level as "desc" (i.e. at the TOP level of the tree)
Q2: How do I (programmatically) add subelements n-times deeper
The resulting file should look something like this:
desc:
- "blahblahblah"
- "desc_trans":
- "Chinese":
- "Mandarin": "blahblahblah"
- "Uyghur": "blahblahblah"
- "Spanish": "blahblahblah"
size: "40k"
Upvotes: 2
Views: 2702
Reputation: 76578
ruamel.yaml
docs are sparse, but what you are trying to do is mostly
on the Python level. You don't change YAML, you load YAML from a file
and parse it into a data structure, change that data structure (using
Python, as ruamel.yaml
is a Python package) and then dump the data
structure back to YAML.
First of all: you cannot get exactly what you want, as your indentation
of sequences is inconsistent. The value for desc
has an indent of six,
with the offset of the block sequence indicator (-
) within that
being four. The value for "desc_trans"
has an indent of four with an
offset of two, and that for "Chinese"
has the minimal indent of
two. ruamel.yaml
has only one pair of sequence indent and offset
values applied to all (block) sequences.
There are a few tricky things you are trying to achieve:
loading a scalar value for the key desc
and magically translating
it into a sequence (with the old scalar value being the first
element). The code should of course make this change only when
necessary.
having a mix of plain scalars combined with scalars with superfluous double quotes. If I see it correctly, any scalar string that is not a key of the root-level mapping should be double quoted. You can achieve that programmatically, but in the example I'll do that by hand.
Assuming Python3:
from pathlib import Path
import sys
import ruamel.yaml
from ruamel.yaml.scalarstring import DoubleQuotedScalarString as dq
yaml = ruamel.yaml.YAML()
yaml.indent(mapping=2, sequence=4, offset=2)
yaml.preserve_quotes = True
data = yaml.load(Path('my_file.yaml'))
assert isinstance(data, dict) # check that the root-level is a dictionary like object
data['size'] = dq('40k')
if not isinstance(data['desc'], list):
data['desc'] = [data['desc']] # change the non-list into a list
l = data['desc']
# make the desc_trans dict first, cannot use dict(desc_trans=[dict(Chinese=[Mandarin=....
desc_trans = []
l.append({dq('desc_trans'): desc_trans})
Chinese = {dq('Chinese'): [{dq('Mandarin'): dq('blahblahblah')}]}
Spanish = {dq('Spanish'): dq('blahblahblah')}
desc_trans.append(Chinese)
desc_trans.append(Spanish)
# the next line also could be simplified using `desc_trans.append()`
data['desc'][1]['desc_trans'][0]['Chinese'].append({dq('Uygur'): dq('blahblahblah')})
# if you want to write to a file use: out = Path('output.yaml')
out = sys.stdout
yaml.dump(data, out)
which gives:
desc:
- "blahblahblah"
- "desc_trans":
- "Chinese":
- "Mandarin": "blahblahblah"
- "Uygur": "blahblahblah"
- "Spanish": "blahblahblah"
size: "40k"
In general I recommend assigning a deep element to a variable and then appending or assigning new keys to that.
As ruamel.yaml
actually loads the mappings in an ordered dictionary subclas,
it is not really necessary to have these sequences of single key-value
pair mappings, that you have as value for "dest_trans"
and for
"Chinese"
. Leaving out the lists simplifies the code, and the subclass has
an .insert()
method to give the necessary control over the key ordering.
Upvotes: 2