dictexporter.py 3.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. class DictExporter(object):
  2. def __init__(self, dictcls=dict, attriter=None, childiter=list, maxlevel=None):
  3. """
  4. Tree to dictionary exporter.
  5. Every node is converted to a dictionary with all instance
  6. attributes as key-value pairs.
  7. Child nodes are exported to the children attribute.
  8. A list of dictionaries.
  9. Keyword Args:
  10. dictcls: class used as dictionary. :any:`dict` by default.
  11. attriter: attribute iterator for sorting and/or filtering.
  12. childiter: child iterator for sorting and/or filtering.
  13. maxlevel (int): Limit export to this number of levels.
  14. >>> from pprint import pprint # just for nice printing
  15. >>> from anytree import AnyNode
  16. >>> from anytree.exporter import DictExporter
  17. >>> root = AnyNode(a="root")
  18. >>> s0 = AnyNode(a="sub0", parent=root)
  19. >>> s0a = AnyNode(a="sub0A", b="foo", parent=s0)
  20. >>> s0b = AnyNode(a="sub0B", parent=s0)
  21. >>> s1 = AnyNode(a="sub1", parent=root)
  22. >>> exporter = DictExporter()
  23. >>> pprint(exporter.export(root)) # order within dictionary might vary!
  24. {'a': 'root',
  25. 'children': [{'a': 'sub0',
  26. 'children': [{'a': 'sub0A', 'b': 'foo'}, {'a': 'sub0B'}]},
  27. {'a': 'sub1'}]}
  28. Pythons dictionary `dict` does not preserve order.
  29. :any:`collections.OrderedDict` does.
  30. In this case attributes can be ordered via `attriter`.
  31. >>> from collections import OrderedDict
  32. >>> exporter = DictExporter(dictcls=OrderedDict, attriter=sorted)
  33. >>> pprint(exporter.export(root))
  34. OrderedDict([('a', 'root'),
  35. ('children',
  36. [OrderedDict([('a', 'sub0'),
  37. ('children',
  38. [OrderedDict([('a', 'sub0A'), ('b', 'foo')]),
  39. OrderedDict([('a', 'sub0B')])])]),
  40. OrderedDict([('a', 'sub1')])])])
  41. The attribute iterator `attriter` may be used for filtering too.
  42. For example, just dump attributes named `a`:
  43. >>> exporter = DictExporter(attriter=lambda attrs: [(k, v) for k, v in attrs if k == "a"])
  44. >>> pprint(exporter.export(root))
  45. {'a': 'root',
  46. 'children': [{'a': 'sub0', 'children': [{'a': 'sub0A'}, {'a': 'sub0B'}]},
  47. {'a': 'sub1'}]}
  48. The child iterator `childiter` can be used for sorting and filtering likewise:
  49. >>> exporter = DictExporter(childiter=lambda children: [child for child in children if "0" in child.a])
  50. >>> pprint(exporter.export(root))
  51. {'a': 'root',
  52. 'children': [{'a': 'sub0',
  53. 'children': [{'a': 'sub0A', 'b': 'foo'}, {'a': 'sub0B'}]}]}
  54. """
  55. self.dictcls = dictcls
  56. self.attriter = attriter
  57. self.childiter = childiter
  58. self.maxlevel = maxlevel
  59. def export(self, node):
  60. """Export tree starting at `node`."""
  61. attriter = self.attriter or (lambda attr_values: attr_values)
  62. return self.__export(node, self.dictcls, attriter, self.childiter)
  63. def __export(self, node, dictcls, attriter, childiter, level=1):
  64. attr_values = attriter(self._iter_attr_values(node))
  65. data = dictcls(attr_values)
  66. maxlevel = self.maxlevel
  67. if maxlevel is None or level < maxlevel:
  68. children = [self.__export(child, dictcls, attriter, childiter, level=level + 1)
  69. for child in childiter(node.children)]
  70. if children:
  71. data['children'] = children
  72. return data
  73. def _iter_attr_values(self, node):
  74. for k, v in node.__dict__.items():
  75. if k in ('_NodeMixin__children', '_NodeMixin__parent'):
  76. continue
  77. yield k, v