Search code examples
pythondictionaryrecursionsvghierarchy

Creating a dictionary using Recursion in Python


ok, after spending way too much time trying to come up with a way to create a dictionary using recursion I think I've realized I can't think of a way of doing it. I'm new to recursion but I believe that's the best way to work with trees and Dictionaries. Below is a sample of the structure I'm trying to create, and this is obtained from and SVG file (XML parser using python) link to svg This is sample of the SVG xml parser I'm using in python:

import xml.etree.ElementTree as ET
path_to_svg = "/images/test/mysvg.svg"
tree = ET.parse(path_to_svg)
root = tree.getroot()           # Getting Root element of the SVG XML
first_layer = root[1]

where firs_layer represents the main layer in the SVG file. The Objective is to be able to identify the groups and and their children based on the SVG structure. ideally It'd be something similar to:

            {
              "items": [
                {
                  "someattribute": {
                    "": "#ffffff",
                    "rule": "evenodd"
                  },
                  "height": "5.1593742",
                  "name": "myunit",
                  "type": "rectangle",
                }
              ],
              "name": "layer1",
              "transform": "translate(-13.956264)",
              "type": "g"  // g stands for group
            }

What I'm really trying to achieve is the tree structure, to be able to group items by hierarchy based on the SVG structure, I feel I've spent too much time on this and I can't figure it out. I'd appreciate some help. Thanks!

def recursiveTree(items, pre=None) -> dict:
    # pre = pre if pre else {}
    # pre = pre[:] if pre else []
    print("i made it here")
    if len(element) <=1 and element.tag != 'title':
        
        return element
    else:
        for item in items:
            # temp = None
            # group = None
            if item.tag == 'g':
                print('made it here')
                group = {"items": [recursiveTree(item, pre)], "name": item.attrib['id'], "transform": item.attrib['transform'],
                        "type":"group"}
                return group
            elif element.tag != 'g':
                if formatstr(element.tag == "path"):
                    stroke, fill = styleParser(style_ie)
                    temp = {'d': element.attrib['d'], 'fill':fill,'name': element.attrib['id'], 'stroke': stroke,
                            'style':{},
                            'type': 'path'}
                    return temp
            else:
                return item
    
print(recursiveTree(root[1]))

this is a sample of what I've tried, but I know that's not the right way to return the structure I need. This is the expected output:

but in case that group had a nested groups, structure would be similar to:

            {
              "items": [
                {
                 "items": [
                  {
                      // This is a group inside a group 
                    "other_rectangle":{
                    "height": "5.1593742",
                    "type": "rectangle",
                    "paint": "#00000",
                     },
                    "mypath":{
                      "d": "m 200.68604,100.33686 1.42273,0.007",
                      "type": "path",
                      "paint" "#fffff"
                     }
                  },
                    "name": "myinnerlayer",
                    "transform": "translate(-13.956264)",
                    "type": "g"  // g stands for group
                    ],
                    "myrectangle":{
                    "height": "5.1593742",
                    "type": "rectangle",
                    "paint": "#00000",
                   }
                },
              "name": "layer1",
              "transform": "translate(-13.956264)",
              "type": "g"  // g stands for group
              ]
            }

Solution

  • If you want to recreate the xml structure as a dict. I'd recommend to store the tag and attributes as key/value pairs and do the same recursively for all children in a nested list:

    import xml.etree.ElementTree as ET
    import json
    
    path_to_svg = "g2273.svg"
    tree = ET.parse(path_to_svg)
    root = tree.getroot()           # Getting Root element of the SVG XML
    
    def get_dict_from_elts(elt):
        d = {'tag': elt.tag}
        d.update(elt.attrib)
        items = [get_dict_from_elts(item) for item in elt]
        if items:
            d['items'] = items 
        return d
    
    print(json.dumps(get_dict_from_elts(root), indent=4))
    

    Output for your provided svg:

    {
        "tag": "{http://www.w3.org/2000/svg}svg",
        "width": "86.657898",
        "height": "76.455795",
        "viewBox": "0 0 22.928236 20.228928",
        "version": "1.1",
        "id": "svg5",
        "items": [
            {
                "tag": "{http://www.w3.org/2000/svg}defs",
                "id": "defs2"
            },
            {
                "tag": "{http://www.w3.org/2000/svg}g",
                "id": "layer1",
                "transform": "translate(-227.12752,-41.445548)",
                "items": [
                    {
                        "tag": "{http://www.w3.org/2000/svg}rect",
                        "style": "fill:#333333;fill-opacity:1;stroke:#000000;stroke-width:0.499996;stroke-linejoin:round",
                        "id": "rect908",
                        "width": "508",
                        "height": "219.7191",
                        "x": "0",
                        "y": "0"
                    },
                    {
                        "tag": "{http://www.w3.org/2000/svg}g",
                        "id": "g2273",
                        "transform": "translate(93.701594,36.124183)",
                        "style": "fill:#ffffff",
                        "items": [
                            {
                                "tag": "{http://www.w3.org/2000/svg}rect",
                                "style": "fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1",                            "id": "ConveyanceStandardBlock-55-1-7",
                                "width": "10.451043",
                                "height": "5.1593747",
                                "x": "145.65312",
                                "y": "19.84375",
                                "items": [
                                    {
                                        "tag": "{http://www.w3.org/2000/svg}title",
                                        "id": "title869-1-9-8"
                                    }
                                ]
                            },
                            {
                                "tag": "{http://www.w3.org/2000/svg}rect",
                                "style": "fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1",                            "id": "ConveyanceStandardBlock-55-1-7-5",
                                "width": "10.451043",
                                "height": "5.1593747",
                                "x": "145.65312",
                                "y": "6.2176895",
                                "items": [
                                    {
                                        "tag": "{http://www.w3.org/2000/svg}title",
                                        "id": "title869-1-9-8-2"
                                    }
                                ]
                            },
                            {
                                "tag": "{http://www.w3.org/2000/svg}g",
                                "id": "layer1-7-7-4",
                                "transform": "matrix(0.36588023,0,0,0.36535713,133.67611,5.571187)",
                                "style": "fill:#ffffff;stroke-width:1.36755;stroke-miterlimit:4;stroke-dasharray:none",
                                "items": [
                                    {
                                        "tag": "{http://www.w3.org/2000/svg}circle",
                                        "style": "fill:#ffffff;stroke:#000000;stroke-width:1.36755;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1",      
                                        "id": "SpiralBase-0-9",
                                        "cx": "27",
                                        "cy": "27",
                                        "r": "27"
                                    },
                                    {
                                        "tag": "{http://www.w3.org/2000/svg}circle",
                                        "style": "fill:#ffffff;stroke:#000000;stroke-width:1.36755;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1",      
                                        "id": "CircleOutline-5-1",
                                        "cx": "27",
                                        "cy": "27",
                                        "r": "20"
                                    },
                                    {
                                        "tag": "{http://www.w3.org/2000/svg}circle",
                                        "style": "fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.36755;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1",
                                        "id": "Circle3-1-3",
                                        "cx": "27",
                                        "cy": "27",
                                        "r": "10"
                                    },
                                    {
                                        "tag": "{http://www.w3.org/2000/svg}path",
                                        "style": "fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.36755;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1",
                                        "d": "m 20.486961,32.275903 6.477822,-10.421566 6.54677,10.421566 h -6.512296 z",
                                        "id": "SpiralDirection-0-4"
                                    }
                                ]
                            }
                        ]
                    }
                ]
            }
        ]
    }