python dict to html unordered list

Question:

Currently im trying to transform this python dict to a html unordered list:

{‘dataStreamId’: ‘raw:com.google.nutrition:NutritionSource’, ‘dataStreamName’: ‘NutritionSource’, ‘type’: ‘raw’, ‘dataType’: {‘name’: ‘com.google.nutrition’, ‘field’: [{‘name’: ‘nutrients’, ‘format’: ‘map’}, {‘name’: ‘meal_type’, ‘format’: ‘integer’, ‘optional’: True}, {‘name’: ‘food_item’, ‘format’: ‘string’, ‘optional’: True}]}, ‘application’: {‘version’: ‘1’, ‘detailsUrl’: ‘http://example.com’, ‘name’: ‘My Example App’}, ‘dataQualityStandard’: []}

with this function:

def dict_to_html_ul(dd, level=4):
    import simplejson
    text = '<ul>'
    for k, v in dd.items():
        text += '<li><b>%s</b>: %s</li>' % (k, dict_to_html_ul(v, level+1) if isinstance(v, (dict)) else (simplejson.dumps(v) if isinstance(v, list) else v))
    text += '</ul>'
    return text

but I am getting this result:

<ul>
    <li><b>dataStreamId</b>: raw:com.google.nutrition:NutritionSource</li>
    <li><b>dataStreamName</b>: NutritionSource</li>
    <li><b>type</b>: raw</li>
    <li><b>dataType</b>: <ul>
            <li><b>name</b>: com.google.nutrition</li>
            <li><b>field</b>: [{"name": "nutrients", "format": "map"}, {"name": "meal_type", "format": "integer",
                "optional": true}, {"name": "food_item", "format": "string", "optional": true}]</li>
        </ul>
    </li>
    <li><b>application</b>: <ul>
            <li><b>version</b>: 1</li>
            <li><b>detailsUrl</b>: http://example.com</li>
            <li><b>name</b>: My Example App</li>
        </ul>
    </li>
    <li><b>dataQualityStandard</b>: []</li>
</ul>

And I am having issues trying to fix the result, basically I wanted to transform the rest of the result in the same way as the function was going.

I tried to transform the text with some string replacing after the function:

text = text.replace('[','').replace(']', '')
text= text.replace('{', '<br>' + '&nbsp;' * level).replace('}', '')
text = text.replace(',', '<br>' + '&nbsp;' * (level-1))

It came out with some extraspacing and I could not replace some parts like this:

"word": "word"

So I tried to make a "re.sub()" but did not have success.

Edit:

Expected output:

<ul>
    <li><b>dataStreamId</b>: raw:com.google.nutrition:NutritionSource</li>
    <li><b>dataStreamName</b>: NutritionSource</li>
    <li><b>type</b>: raw</li>
    <li><b>dataType</b>:
        <ul>
            <li><b>name</b>: com.google.nutrition</li>
            <li><b>field</b>:
                <ul>
                    <li><b>name</b>: nutrients</li>
                    <li><b>format</b>: map</li>
                    <br>
                    <li><b>name</b>: meal_type</li>
                    <li><b>format</b>: integer</li>
                    <li><b>optional</b>: true</li>
                    <br>
                    <li><b>name</b>: food_item</li>
                    <li><b>format</b>: string</li>
                    <li><b>optional</b>: true</li>
                </ul>
            </li>
        </ul>
    </li>
    <li><b>application</b>: <ul>
            <li><b>version</b>: 1</li>
            <li><b>detailsUrl</b>: http://example.com</li>
            <li><b>name</b>: My Example App</li>
        </ul>
    </li>
    <li><b>dataQualityStandard</b>: []</li>
</ul>

Edit2:
Thanks for the answer @AndrejKesely

I actually got the proper html in the first specific dict, but I have another dict that didnt actually work with this function: –

a = {'minStartTimeNs': '1573159699023000000', 'maxEndTimeNs': '1573159699023999000', 'dataSourceId': 'raw:com.google.nutrition:NutritionSource', 'point': [{'startTimeNanos': '1573159699023000000', 'endTimeNanos': '1573159699023999000', 'dataTypeName': 'com.google.nutrition', 'value': [{'mapVal': [{'key': 'fat.total', 'value': {'fpVal': 0.4}}, {'key': 'sodium', 'value': {'fpVal': 1}}, {'key': 'fat.saturated', 'value': {
'fpVal': 0.1}}, {'key': 'protein', 'value': {'fpVal': 1.3}}, {'key': 'carbs.total', 'value': {'fpVal': 27}}, {'key': 'cholesterol', 'value': {'fpVal': 0}}, {'key': 'calories', 'value': {'fpVal': 105}}, {'key': 'sugar', 'value': {'fpVal': 14}}, {'key': 'dietary_fiber', 'value': {'fpVal': 3.1}}, {'key': 'potassium', 'value': {'fpVal': 422}}]}, {'intVal': 4, 'mapVal': []}, {'stringVal': 'apple', 'mapVal': []}]}]}

I was expecting a function that could work for both

but the output with get_html() in the other dict outputs:

<ul>
    <li><b>minStartTimeNs</b>: 1573159699023000000</li>
    <li><b>maxEndTimeNs</b>: 1573159699023999000</li>
    <li><b>dataSourceId</b>: raw:com.google.nutrition:NutritionSource</li>
    <li><b>point</b>: <ul>
            <li><b>startTimeNanos</b>: 1573159699023000000</li>
            <li><b>endTimeNanos</b>: 1573159699023999000</li>
            <li><b>dataTypeName</b>: com.google.nutrition</li>
            <li><b>value</b>: [{'mapVal': [{'key': 'fat.total', 'value': {'fpVal': 0.4}}, {'key': 'sodium', 'value':
                {'fpVal': 1}}, {'key': 'fat.saturated', 'value': {'fpVal': 0.1}}, {'key': 'protein', 'value': {'fpVal':
                1.3}}, {'key': 'carbs.total', 'value': {'fpVal': 27}}, {'key': 'cholesterol', 'value': {'fpVal': 0}},
                {'key': 'calories', 'value': {'fpVal': 105}}, {'key': 'sugar', 'value': {'fpVal': 14}}, {'key':
                'dietary_fiber', 'value': {'fpVal': 3.1}}, {'key': 'potassium', 'value': {'fpVal': 422}}]}, {'intVal':
                4, 'mapVal': []}, {'stringVal': 'apple', 'mapVal': []}]</li>
        </ul>
    </li>
</ul>

And I was expecting:

<ul>
    <li><b>minStartTimeNs</b>: 1573159699023000000</li>
    <li><b>maxEndTimeNs</b>: 1573159699023999000</li>
    <li><b>dataSourceId</b>: raw:com.google.nutrition:NutritionSource</li>
    <li><b>point</b>: <ul>
            <li><b>startTimeNanos</b>: 1573159699023000000</li>
            <li><b>endTimeNanos</b>: 1573159699023999000</li>
            <li><b>dataTypeName</b>: com.google.nutrition</li>
            <li><b>value</b>:
                <ul>
                    <li><b>mapVal</b>:
                    <ul>
                        <li><b>key</b>: fat.total</li>
                        <li><b>value</b>:
                            <ul>
                                <li><b>fpVal</b>: 0.4</li>
                            </ul>
                        </li>
                        <br>
                        <li><b>key</b>: sodium</li>
                        <li><b>value</b>:
                            <ul>
                                <li><b>fpVal</b>: 1</li>
                            </ul>
                        </li>
                        <br>
                        <li><b>key</b>:</li> 'fat.saturated',
                        <li><b>value</b>:
                            <ul>
                                <li><b>fpVal</b>: 0.4</li>
                            </ul>
                        </li>
                        <br>
                        <li><b>key</b>:</li> 'protein',
                        <li><b>value</b>:
                            <ul>
                                <li><b>fpVal</b>: 5.4</li>
                            </ul>
                        </li>
                        <br>
                        <li><b>key</b>:</li> 'carbs.total',
                        <li><b>value</b>:
                            <ul>
                                <li><b>fpVal</b>: 6.4</li>
                            </ul>
                        </li>
                        <br>
                        <li><b>key</b>:</li> 'cholesterol',
                        <li><b>value</b>:
                            <ul>
                                <li><b>fpVal</b>: 4.5</li>
                            </ul>
                        </li>
                        <br>
                        <li><b>key</b>:</li> 'calories',
                        <li><b>value</b>:
                            <ul>
                                <li><b>fpVal</b>: 3.4</li>
                            </ul>
                        </li>
                        <br>
                        <li><b>key</b>:</li> 'sugar',
                        <li><b>value</b>:
                            <ul>
                                <li><b>fpVal</b>: 5.5</li>
                            </ul>
                        </li>
                        <br>
                        <li><b>key</b>:</li>'dietary_fiber',
                        <li><b>value</b>:
                            <ul>
                                <li><b>fpVal</b>: 1</li>
                            </ul>
                        </li>
                        <br>
                        <li><b>key</b>:</li> 'potassium',
                        <li><b>value</b>:
                            <ul>
                                <li><b>fpVal</b>: 2</li>
                            </ul>
                        </li>
                        <br>
                    </ul>
                    <li><b>intVal</b>: 4</li>
                    <li><b>mapVal</b>: []</li>
                    <br>
                    <li><b>stringVal</b>: apple</li>
                    <li><b>mapVal</b>: []</li>
                </li>
                </ul>
            </li>
        </ul>
    </li>
</ul>
Asked By: docksdocks

||

Answers:

Try:

dct = {
    "dataStreamId": "raw:com.google.nutrition:NutritionSource",
    "dataStreamName": "NutritionSource",
    "type": "raw",
    "dataType": {
        "name": "com.google.nutrition",
        "field": [
            {"name": "nutrients", "format": "map"},
            {"name": "meal_type", "format": "integer", "optional": True},
            {"name": "food_item", "format": "string", "optional": True},
        ],
    },
    "application": {
        "version": "1",
        "detailsUrl": "http://example.com",
        "name": "My Example App",
    },
    "dataQualityStandard": [],
}


def get_html(o):
    s = ""

    if isinstance(o, dict):
        s += "<ul>n"

        for k, v in o.items():
            s += f"<li><b>{k}</b>: " + get_html(v) + "</li>n"

        s += "</ul>"
    elif isinstance(o, list):
        s += "<ul>n"

        if not o:
            return str(o)
        else:
            out = []
            for v in o:
                ss = ""
                for kk, vv in v.items():
                    ss += f"<li><b>{kk}</b>: {vv}</li>n"
                out.append(ss)

            s += "<br>n".join(out)
            s += "</ul>"
    else:
        return str(o)

    return s


print(get_html(dct))

Prints ("beautified" result):

<ul>
    <li><b>dataStreamId</b>: raw:com.google.nutrition:NutritionSource</li>
    <li><b>dataStreamName</b>: NutritionSource</li>
    <li><b>type</b>: raw</li>
    <li><b>dataType</b>:
        <ul>
            <li><b>name</b>: com.google.nutrition</li>
            <li><b>field</b>:
                <ul>
                    <li><b>name</b>: nutrients</li>
                    <li><b>format</b>: map</li>
                    <br>
                    <li><b>name</b>: meal_type</li>
                    <li><b>format</b>: integer</li>
                    <li><b>optional</b>: True</li>
                    <br>
                    <li><b>name</b>: food_item</li>
                    <li><b>format</b>: string</li>
                    <li><b>optional</b>: True</li>
                </ul>
            </li>
        </ul>
    </li>
    <li><b>application</b>:
        <ul>
            <li><b>version</b>: 1</li>
            <li><b>detailsUrl</b>: http://example.com</li>
            <li><b>name</b>: My Example App</li>
        </ul>
    </li>
    <li><b>dataQualityStandard</b>: []</li>
</ul>
Answered By: Andrej Kesely

Got this function to work thanks to @AndrejKesely for basically crushing it

def get_html(o):
    s = ""
    if isinstance(o, dict):
        s += "<ul>n"
        for k, v in o.items():
            s += f"<li><b>{k}</b>: " + get_html(v) + "</li>n"
        s += "</ul>"
    elif isinstance(o, list):
        s += "<ul>n"
        if not o:
            return str(o)
        else:
            out = []
            for v in o:
                ss = ""
                for kk, vv in v.items():
                    ss += f"<li><b>{kk}</b>:" + get_html(vv) + " </li>n"
                out.append(ss)
            s += "<br>n".join(out)
            s += "</ul>"
    else:
        return str(o)
    return s

The only thing I changed in his function so it could work in any list/dict was make this:

            for v in o:
                ss = ""
                for kk, vv in v.items():
                    ss += f"<li><b>{kk}</b>: {vv}</li>n"
                out.append(ss)

become iterative like this:

            for v in o:
                ss = ""
                for kk, vv in v.items():
                    ss += f"<li><b>{kk}</b>:" + get_html(vv) + " </li>n"
                out.append(ss)
Answered By: docksdocks