43 lines
1.4 KiB
Python
43 lines
1.4 KiB
Python
"""Util functions used by SSDP."""
|
|
from __future__ import annotations
|
|
|
|
from collections import defaultdict
|
|
from typing import Any
|
|
|
|
from defusedxml import ElementTree
|
|
|
|
|
|
# Adapted from http://stackoverflow.com/a/10077069
|
|
# to follow the XML to JSON spec
|
|
# https://www.xml.com/pub/a/2006/05/31/converting-between-xml-and-json.html
|
|
def etree_to_dict(tree: ElementTree) -> dict[str, dict[str, Any] | None]:
|
|
"""Convert an ETree object to a dict."""
|
|
# strip namespace
|
|
tag_name = tree.tag[tree.tag.find("}") + 1 :]
|
|
|
|
tree_dict: dict[str, dict[str, Any] | None] = {
|
|
tag_name: {} if tree.attrib else None
|
|
}
|
|
children = list(tree)
|
|
if children:
|
|
child_dict: dict[str, list] = defaultdict(list)
|
|
for child in map(etree_to_dict, children):
|
|
for k, val in child.items():
|
|
child_dict[k].append(val)
|
|
tree_dict = {
|
|
tag_name: {k: v[0] if len(v) == 1 else v for k, v in child_dict.items()}
|
|
}
|
|
dict_meta = tree_dict[tag_name]
|
|
if tree.attrib:
|
|
assert dict_meta is not None
|
|
dict_meta.update(("@" + k, v) for k, v in tree.attrib.items())
|
|
if tree.text:
|
|
text = tree.text.strip()
|
|
if children or tree.attrib:
|
|
if text:
|
|
assert dict_meta is not None
|
|
dict_meta["#text"] = text
|
|
else:
|
|
tree_dict[tag_name] = text
|
|
return tree_dict
|