Search code examples
pythonstringlistdictionarygoogle-deployment-manager

Python - Convert List Of Dictionaries & Key Values To String


Take the following list of dictionaries and key value pairs:

              [{'name': 'test-project',
                'properties': {'name': 'test-project',
                               'parent': {'id': '', 'type': 'folder'},
                               'projectId': 'test-project'},
                'type': 'cloudresourcemanager.v1.project'},
               {'metadata': {'dependsOn': ['test-project']},
                'name': 'billing_test-project',
                'properties': {'billingAccountName': 'billingAccountName',
                               'name': 'projects/test-project'},
                'type': 'deploymentmanager.v2.virtual.projectBillingInfo'},
               {'name': 'apis',
                'properties': {'apis': ['compute.googleapis.com'],
                               'billing': 'billing_test-project',
                               'concurrent_api_activation': True,
                               'project': 'test-project'},
                'type': 'apis.py'},
               {'name': 'service-accounts',
                'properties': {'project': 'test-project',
                               'service-accounts': ''},
                'type': 'service-accounts.py'},
               {'action': 'gcp-types/compute-v1:compute.projects.setUsageExportBucket',
                'metadata': {'dependsOn': ['test-project',
                                           'test-project-compute.googleapis.com']},
                'name': 'set-export-bucket',
                'properties': {'bucketName': 'gs://usage-exports',
                               'project': 'test-project',
                               'reportNamePrefix': 'usage_gce_'}}]}

I need to convert this into the following syntax:

resources:\n- name: test-project\n properties:\n name: test-project\n parent:\n id:\n type: folder\n

I thought perhaps something like the following would work following my brief Google search:

'\n'.join(d for d in resources)

Unfortunately, that then gives me the error: "TypeError: sequence item 0: expected str instance, dict found"

Any help with this would be greatly appreciated.

(As a side note, Google's only example of the config content being a string is for creating a VM: https://cloud.google.com/deployment-manager/docs/deployments#api; I notice that the spaces seem to increase with each key value pair, but I'm not entirely sure if that's actually required here or not).

EDIT: Apologies, I meant to say I need the key value pairs in a similar format to the below, as a string:

resource = "resources:\n- name: vm-created-by-cloud-config\n  type: compute.v1.instance\n  properties:\n    zone: us-central1-a\n    machineType: https://www.googleapis.com/compute/v1/projects/myproject/zones/us-central1-a/machineTypes/n1-standard-1\n    disks:\n    - deviceName: boot\n      type: PERSISTENT\n      boot: true\n      autoDelete: true\n      initializeParams:\n        diskName: disk-created-by-cloud-config\n        sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-wheezy-v20151104\n    networkInterfaces:\n    - network: https://www.googleapis.com/compute/v1/projects/myproject/global/networks/default\n"

So, when printed, it looks like this:

resources:
- name: test-project
  properties:
    name: test-project
    parent:
      id:
      type: folder
    projectId: test-project
  type: cloudresourcemanager.v1.project
- metadata:
    dependsOn: test-project
  name: billing_test-project
  properties:
    billingAccountName: billingAccountName
    name: projects/test-project
  type: deploymentmanager.v2.virtual.projectBillingInfo
- name: apis
  properties:
    apis: compute.googleapis.com
    billing: billing_test-project
    concurrent_api_activation: True
    project: test-project
  type: apis.py
- name: service-accounts
  properties:
    project: test-project
    service-accounts:
  type: service-accounts.py
- action: gcp-types/compute-v1:compute.projects.setUsageExportBucket
  metadata:
    dependsOn: test-project,test-project-compute.googleapis.com
  name: set-export-bucket
  properties:
    bucketName: gs://usage-exports
    project: test-project
    reportNamePrefix: usage_gce_

Solution

  • Try applying the following recursive function. This should work for your specific use case:

    resources = {'resources': [{'name': 'test-project',
                    'properties': {'name': 'test-project',
                                   'parent': {'id': '', 'type': 'folder'},
                                   'projectId': 'test-project'},
                    'type': 'cloudresourcemanager.v1.project'},
                   {'metadata': {'dependsOn': ['test-project']},
                    'name': 'billing_test-project',
                    'properties': {'billingAccountName': 'billingAccountName',
                                   'name': 'projects/test-project'},
                    'type': 'deploymentmanager.v2.virtual.projectBillingInfo'},
                   {'name': 'apis',
                    'properties': {'apis': ['compute.googleapis.com'],
                                   'billing': 'billing_test-project',
                                   'concurrent_api_activation': True,
                                   'project': 'test-project'},
                    'type': 'apis.py'},
                   {'name': 'service-accounts',
                    'properties': {'project': 'test-project',
                                   'service-accounts': ''},
                    'type': 'service-accounts.py'},
                   {'action': 'gcp-types/compute-v1:compute.projects.setUsageExportBucket',
                    'metadata': {'dependsOn': ['test-project',
                                               'test-project-compute.googleapis.com']},
                    'name': 'set-export-bucket',
                    'properties': {'bucketName': 'gs://usage-exports',
                                   'project': 'test-project',
                                   'reportNamePrefix': 'usage_gce_'}}]}
    
    
    def unpack_dict(d, spaces=0):
        try:
            s = ' ' * spaces
            spaces += 2
            return  ' '.join([f'\n{s}{k}: {unpack_dict(v, spaces)}' for k, v in d.items()])
        except AttributeError:
            if isinstance(d, list):
                return ''.join([unpack_dict(item) for item in d])
            else:
                return d
    
    
    result = unpack_dict(resources).strip()
    

    output for print(result)

    resources: 
    name: test-project 
    properties: 
      name: test-project 
      parent: 
        id:  
        type: folder 
      projectId: test-project 
    type: cloudresourcemanager.v1.project
    metadata: 
      dependsOn: test-project 
    name: billing_test-project 
    properties: 
      billingAccountName: billingAccountName 
      name: projects/test-project 
    type: deploymentmanager.v2.virtual.projectBillingInfo
    name: apis 
    properties: 
      apis: compute.googleapis.com 
      billing: billing_test-project 
      concurrent_api_activation: True 
      project: test-project 
    type: apis.py
    name: service-accounts 
    properties: 
      project: test-project 
      service-accounts:  
    type: service-accounts.py
    action: gcp-types/compute-v1:compute.projects.setUsageExportBucket 
    metadata: 
      dependsOn: test-projecttest-project-compute.googleapis.com 
    name: set-export-bucket 
    properties: 
      bucketName: gs://usage-exports 
      project: test-project 
      reportNamePrefix: usage_gce_
    

    Please note:

    1. If you can't use F-strings due to your Python version (< 3.6), you can use the format() function of the str class.
    2. If you need those hyphens, like the one prepending "name" (which I'm not clear enough where else they should go in the output), you could create a temporal dictionary with those included in the keys where they are supposed to be, and apply the unpack_dict() function I provided, passing that temporal dictionary instead of the original resources.