0

I need to convert json to given hcl structure. There is this code:

user_dicts = { 'users': [ {'name': 'test1', 'pass': 'password1', 'permissions': [ {'access': 'yes', 'role': 'admin'}, {'access': 'yes', 'role': 'user'} ] }, {'name': 'test2', 'pass': 'password2', 'permissions': [ {'access': 'yes', 'role': 'admin'} ] } ] }

double_q = '"'

result = f"""
users = [
    {''.join([
    f'''{{
        name        = {double_q}{ d['name'] }{double_q},
        pass        = {double_q}{ d['pass'] }{double_q},
        permissions = [
            {''.join([f"{{ access = {double_q}{ d['permissions'][index]['access'] }{double_q}, role = {double_q}{ d['permissions'][index]['role'] }{double_q} }}," for index in range(len(d['permissions'])) ])}
        ]
    }},
    ''' for d in user_dicts['users']
    ])}
]"""

print(result)

which returns the following line:

users = [
    {
        name        = "test1",
        pass        = "password1",
        permissions = [
            { access = "yes", role = "admin" },{ access = "yes", role = "user" },
        ]
    },
    {
        name        = "test2",
        pass        = "password2",
        permissions = [
            { access = "yes", role = "admin" },
        ]
    },
    
]

What needs to be done so that there is a new line between the dictionaries in permissions while maintaining the number of spaces? That is:

users = [
    {
        name        = "test1",
        pass        = "password1",
        permissions = [
            { access = "yes", role = "admin" },
            { access = "yes", role = "user" },
        ]
    },
    {
        name        = "test2",
        pass        = "password2",
        permissions = [
            { access = "yes", role = "admin" },
        ]
    },
    
]

I tried adding f-string to new_line variable:

user_dicts = { 'users': [ {'name': 'test1', 'pass': 'password1', 'permissions': [ {'access': 'yes', 'role': 'admin'}, {'access': 'yes', 'role': 'user'} ] }, {'name': 'test2', 'pass': 'password2', 'permissions': [ {'access': 'yes', 'role': 'admin'} ] } ] }

double_q = '"'
new_line = '\n'

result = f"""
users = [
    {''.join([
    f'''{{
        name        = {double_q}{ d['name'] }{double_q},
        pass        = {double_q}{ d['pass'] }{double_q},
        permissions = [
            {f'{new_line}'.join([f"{{ access = {double_q}{ d['permissions'][index]['access'] }{double_q}, role = {double_q}{ d['permissions'][index]['role'] }{double_q} }}," for index in range(len(d['permissions'])) ])}
        ]
    }},
    ''' for d in user_dicts['users']
    ])}
]"""

print(result)

But the next line becomes from the beginning of the line:

users = [
    {
        name        = "test1",
        pass        = "password1",
        permissions = [
            { access = "yes", role = "admin" },
{ access = "yes", role = "user" },
        ]
    },
    {
        name        = "test2",
        pass        = "password2",
        permissions = [
            { access = "yes", role = "admin" },
        ]
    },
    
]
RMNTRVN
  • 63
  • 1
  • 5

1 Answers1

1

Quick fix

You were on the right track, using the new_line variable together with str.join(). To maintain the number of spaces, just include them in new_line:

new_line = '            \n'

Further thinking

You may want to start using a templating engine, like Jinja.

Pros

  • You don't need to concern yourself with (nested) f-strings
  • The template string looks like your desired output (kinda WYSIWYG)
  • Whitespace control is easy

Cons

  • You'll probably have to spend some time learning how to use the templating engine

Your example, implemented with Jinja

To run the following code, you need to install the jinja2 package

import jinja2

user_dicts = { 'users': [ {'name': 'test1', 'pass': 'password1', 'permissions': [ {'access': 'yes', 'role': 'admin'}, {'access': 'yes', 'role': 'user'} ] }, {'name': 'test2', 'pass': 'password2', 'permissions': [ {'access': 'yes', 'role': 'admin'} ] } ] }

template_str = """
users = [
{% for user in users %}
    {
        name        = "{{ user['name'] }}",
        pass        = "{{ user['pass'] }}",
        permissions = [
{% for p in user['permissions'] %}
            { access = "{{ p['access'] }}", role = "{{ p['role'] }}" },
{% endfor %}
        ]
    },
{% endfor %}

]
""".strip()

template = jinja2.Template(template_str, trim_blocks=True, lstrip_blocks=True)
users = template.render(user_dicts).replace("'", '"')

The template string template_str is basically your desired output, plus some special tokens ({% for ... %}, {% endfor %}, {{ ... }}). These tokens tell the engine how to "fill in the blanks".