Search code examples
pythonyamljinja2

Taking a list from dictionary in Python and dynamically exploding it to YAML using Jinja2 template?


I am trying to populate a YAML file with values from a dictionary in python.

from jinja2 import Environment, FileSystemLoader
import glob

def map_template(context,template_path,template_name,destination):

    environment = Environment(loader = FileSystemLoader(template_path),   trim_blocks=True, lstrip_blocks=True)
    results_filename = destination
    results_template = environment.get_template(template_name)
    with open(results_filename, mode="w", encoding="utf-8") as results:
        results.write(results_template.render(context))
        print(f"... wrote {results_filename}")

The value in the context looks like

 {'node' : {'value' : [{'key1':val1,'key2':"str2",'key3':val3}] } }

The wanted output YAML looks like

node:
     - key1:val1
       key2:"str2"
       key3: val3

My current solution in my template

node:
      - {{node.value[0].key1}}
        {{node.value[0].key2}}
        {{node.value[0].key3}}

But is there a way to dynamically explode the list using jinja2 template without having to hardcode the number of elements and their names?


Solution

  • One complicating factor here is that your data structure doesn't match your desired output. Rather than trying to produce YAML through a templating process, you should first generate the appropriate data structure. If you have something like:

    data = {
        "node": {
            "value": [
                {"key1": "val1", "key2": "str2", "key3": "val3"},
            ]
        }
    }
    context = {"node_value": {"node": data["node"]["value"]}}
    

    Then you could register a custom to_yaml filter that would use the yaml module to produce the desired output:

    from jinja2 import Environment, FileSystemLoader
    import yaml
    
    
    def to_yaml(val):
        return yaml.safe_dump(val)
    
    
    data = {
        "node": {
            "value": [
                {"key1": "val1", "key2": "str2", "key3": "val3"},
            ]
        }
    }
    context = {"node_value": {"node": data["node"]["value"]}}
    
    
    environment = Environment(
        loader=FileSystemLoader("."), trim_blocks=True, lstrip_blocks=True
    )
    environment.filters["to_yaml"] = to_yaml
    
    results_template = environment.get_template("example.j2")
    with open("example.txt", mode="w", encoding="utf-8") as results:
        results.write(results_template.render(context))
    

    Given this content in example.j2:

    {{ node_value|to_yaml }}
    

    The above code produces the following example.txt:

    node:
    - key1: val1
      key2: str2
      key3: val3
    

    If for some reason you can't modify your context value, you could do something like this instead in your template:

    node:
    {{ node.value|to_yaml }}
    

    Which will also produce:

    node:
    - key1: val1
      key2: str2
      key3: val3
    

    And of course you can use things like the indent filter if you want the content shift over as in your question.