0
# Data Import and Export
1
2
Export tree structures to various formats (dict, JSON, DOT for Graphviz, Mermaid) and import from structured data formats for data exchange, visualization, and interoperability with other systems.
3
4
## Export Capabilities
5
6
### DictExporter - Dictionary Export
7
8
Export tree structures to Python dictionary format for serialization, data processing, or conversion to other formats.
9
10
```python { .api }
11
class DictExporter:
12
"""
13
Export tree to dictionary format.
14
15
Args:
16
dictcls: Dictionary class to use (default dict)
17
attriter: Function to filter/transform node attributes
18
childiter: Function to determine child iteration order (default list)
19
maxlevel: Maximum depth to export (default None - export all)
20
"""
21
def __init__(self, dictcls=dict, attriter=None, childiter=list, maxlevel=None): ...
22
23
def export(self, node): ...
24
```
25
26
**Usage Example:**
27
28
```python
29
from anytree import Node, DictExporter
30
31
# Create tree structure
32
root = Node("Company", founded=2020)
33
engineering = Node("Engineering", parent=root, budget=100000)
34
marketing = Node("Marketing", parent=root, budget=50000)
35
Node("Backend", parent=engineering, team_size=5)
36
Node("Frontend", parent=engineering, team_size=3)
37
38
# Basic dictionary export
39
exporter = DictExporter()
40
data = exporter.export(root)
41
print(data)
42
# {'children': [
43
# {'budget': 100000, 'children': [
44
# {'name': 'Backend', 'team_size': 5},
45
# {'name': 'Frontend', 'team_size': 3}
46
# ], 'name': 'Engineering'},
47
# {'budget': 50000, 'name': 'Marketing'}
48
# ], 'founded': 2020, 'name': 'Company'}
49
50
# Custom attribute filtering
51
def attr_filter(attrs):
52
# Only export specific attributes
53
return [(k, v) for k, v in attrs if k in ['name', 'budget']]
54
55
filtered_exporter = DictExporter(attriter=attr_filter)
56
filtered_data = filtered_exporter.export(root)
57
print(filtered_data) # Only name and budget attributes
58
59
# Limit export depth
60
shallow_exporter = DictExporter(maxlevel=2)
61
shallow_data = shallow_exporter.export(root)
62
print(f"Levels exported: {count_levels(shallow_data)}") # Max 2 levels
63
```
64
65
### JsonExporter - JSON Export
66
67
Export tree structures to JSON format for web APIs, configuration files, or data storage.
68
69
```python { .api }
70
class JsonExporter:
71
"""
72
Export tree to JSON format.
73
74
Args:
75
dictcls: Dictionary class to use (default dict)
76
attriter: Function to filter/transform node attributes
77
childiter: Function to determine child iteration order (default list)
78
maxlevel: Maximum depth to export (default None - export all)
79
**kwargs: Additional arguments passed to json.dumps()
80
"""
81
def __init__(self, dictcls=dict, attriter=None, childiter=list, maxlevel=None, **kwargs): ...
82
83
def export(self, node): ...
84
```
85
86
**Usage Example:**
87
88
```python
89
from anytree import Node, JsonExporter
90
import json
91
92
# Create tree with various data types
93
root = Node("API", version="1.0")
94
users = Node("users", parent=root, endpoints=["GET", "POST", "PUT"])
95
posts = Node("posts", parent=root, endpoints=["GET", "POST"])
96
Node("profile", parent=users, auth_required=True)
97
Node("settings", parent=users, auth_required=True)
98
99
# Basic JSON export
100
exporter = JsonExporter()
101
json_data = exporter.export(root)
102
print(json_data) # JSON string
103
104
# Pretty printed JSON
105
pretty_exporter = JsonExporter(indent=2, sort_keys=True)
106
pretty_json = pretty_exporter.export(root)
107
print(pretty_json)
108
109
# Parse back to dict for verification
110
parsed = json.loads(json_data)
111
print(f"Root name: {parsed['name']}")
112
print(f"Users endpoints: {parsed['children'][0]['endpoints']}")
113
```
114
115
### DotExporter - Graphviz DOT Export
116
117
Export tree structures to DOT format for visualization with Graphviz, creating professional tree diagrams and charts.
118
119
```python { .api }
120
class DotExporter:
121
"""
122
Export tree to Graphviz DOT format.
123
124
Args:
125
node: Root node for export
126
graph: Graph type ("digraph" or "graph")
127
name: Graph name
128
options: Global graph options
129
indent: Indentation string
130
nodenamefunc: Function to generate node names
131
nodeattrfunc: Function to generate node attributes
132
edgeattrfunc: Function to generate edge attributes
133
edgetypefunc: Function to determine edge types
134
"""
135
def __init__(self, node, graph="digraph", name="tree", options=None, indent=" ",
136
nodenamefunc=None, nodeattrfunc=None, edgeattrfunc=None, edgetypefunc=None): ...
137
138
def export(self, node): ...
139
def to_dotfile(self, filename): ...
140
def to_picture(self, filename, **kwargs): ...
141
```
142
143
**Usage Example:**
144
145
```python
146
from anytree import Node, DotExporter
147
148
# Create organizational tree
149
root = Node("CEO", title="Chief Executive")
150
cto = Node("CTO", parent=root, title="Chief Technology Officer")
151
cfo = Node("CFO", parent=root, title="Chief Financial Officer")
152
Node("Backend Lead", parent=cto, title="Senior Engineer")
153
Node("Frontend Lead", parent=cto, title="Senior Engineer")
154
Node("Accountant", parent=cfo, title="Financial Analyst")
155
156
# Basic DOT export
157
exporter = DotExporter(root)
158
dot_output = exporter.export(root)
159
print(dot_output)
160
161
# Custom node attributes for styling
162
def node_attr(node):
163
attrs = [f'label="{node.name}\\n{getattr(node, "title", "")}"']
164
if node.name == "CEO":
165
attrs.append('shape=box')
166
attrs.append('style=filled')
167
attrs.append('fillcolor=lightblue')
168
return ", ".join(attrs)
169
170
styled_exporter = DotExporter(root, nodeattrfunc=node_attr)
171
styled_dot = styled_exporter.export(root)
172
173
# Save to file and generate image
174
styled_exporter.to_dotfile("org_chart.dot")
175
styled_exporter.to_picture("org_chart.png", format="png", prog="dot")
176
```
177
178
### UniqueDotExporter - Unique Node DOT Export
179
180
DOT exporter that ensures unique node identifiers, useful for trees with duplicate node names.
181
182
```python { .api }
183
class UniqueDotExporter(DotExporter):
184
"""
185
DOT exporter ensuring unique node identifiers.
186
Handles cases where multiple nodes have the same name.
187
"""
188
def __init__(self, node, **kwargs): ...
189
```
190
191
**Usage Example:**
192
193
```python
194
from anytree import Node, UniqueDotExporter
195
196
# Tree with duplicate names
197
root = Node("Department")
198
team1 = Node("Team", parent=root, location="Building A")
199
team2 = Node("Team", parent=root, location="Building B")
200
Node("Member", parent=team1, employee_id=101)
201
Node("Member", parent=team2, employee_id=102)
202
203
# Regular exporter might have issues with duplicate names
204
unique_exporter = UniqueDotExporter(root)
205
unique_dot = unique_exporter.export(root)
206
print(unique_dot) # Each node gets unique identifier
207
```
208
209
### MermaidExporter - Mermaid Diagram Export
210
211
Export tree structures to Mermaid diagram format for modern web-based visualization and documentation.
212
213
```python { .api }
214
class MermaidExporter:
215
"""
216
Export tree to Mermaid diagram format.
217
218
Args:
219
indent: Indentation string
220
nodenamefunc: Function to generate node names
221
nodeattrfunc: Function to generate node attributes
222
edgeattrfunc: Function to generate edge attributes
223
"""
224
def __init__(self, indent=" ", nodenamefunc=None, nodeattrfunc=None, edgeattrfunc=None): ...
225
226
def export(self, node): ...
227
```
228
229
**Usage Example:**
230
231
```python
232
from anytree import Node, MermaidExporter
233
234
# Create process flow tree
235
root = Node("Start")
236
decision = Node("Decision", parent=root)
237
path_a = Node("Path A", parent=decision)
238
path_b = Node("Path B", parent=decision)
239
Node("End A", parent=path_a)
240
Node("End B", parent=path_b)
241
242
# Export to Mermaid format
243
exporter = MermaidExporter()
244
mermaid_output = exporter.export(root)
245
print(mermaid_output)
246
# graph TD
247
# Start --> Decision
248
# Decision --> "Path A"
249
# Decision --> "Path B"
250
# "Path A" --> "End A"
251
# "Path B" --> "End B"
252
253
# Custom node attributes
254
def node_attr(node):
255
if "End" in node.name:
256
return "shape=circle"
257
elif node.name == "Decision":
258
return "shape=diamond"
259
return ""
260
261
styled_exporter = MermaidExporter(nodeattrfunc=node_attr)
262
styled_mermaid = styled_exporter.export(root)
263
```
264
265
## Import Capabilities
266
267
### DictImporter - Dictionary Import
268
269
Import tree structures from Python dictionary format, useful for deserializing exported data or creating trees from configuration files.
270
271
```python { .api }
272
class DictImporter:
273
"""
274
Import tree from dictionary format.
275
276
Args:
277
nodecls: Node class to use for imported nodes (default AnyNode)
278
childiter: Attribute name for children list (default "children")
279
"""
280
def __init__(self, nodecls=AnyNode, childiter="children"): ...
281
282
def import_(self, data): ...
283
```
284
285
**Usage Example:**
286
287
```python
288
from anytree import DictImporter, RenderTree
289
290
# Dictionary data (e.g., from JSON file or API)
291
tree_data = {
292
"name": "Company",
293
"founded": 2020,
294
"children": [
295
{
296
"name": "Engineering",
297
"budget": 100000,
298
"children": [
299
{"name": "Backend", "team_size": 5},
300
{"name": "Frontend", "team_size": 3}
301
]
302
},
303
{
304
"name": "Marketing",
305
"budget": 50000,
306
"children": []
307
}
308
]
309
}
310
311
# Import dictionary to tree
312
importer = DictImporter()
313
root = importer.import_(tree_data)
314
315
print(RenderTree(root))
316
# AnyNode(founded=2020, name='Company')
317
# ├── AnyNode(budget=100000, name='Engineering')
318
# │ ├── AnyNode(name='Backend', team_size=5)
319
# │ └── AnyNode(name='Frontend', team_size=3)
320
# └── AnyNode(budget=50000, name='Marketing')
321
322
# Access imported attributes
323
print(f"Company founded: {root.founded}")
324
engineering = root.children[0]
325
print(f"Engineering budget: ${engineering.budget}")
326
```
327
328
### JsonImporter - JSON Import
329
330
Import tree structures from JSON format, combining JSON parsing with dictionary import for direct JSON-to-tree conversion.
331
332
```python { .api }
333
class JsonImporter:
334
"""
335
Import tree from JSON format.
336
337
Args:
338
nodecls: Node class to use for imported nodes (default AnyNode)
339
childiter: Attribute name for children list (default "children")
340
"""
341
def __init__(self, nodecls=AnyNode, childiter="children"): ...
342
343
def import_(self, data): ...
344
```
345
346
**Usage Example:**
347
348
```python
349
from anytree import JsonImporter, Node, RenderTree
350
import json
351
352
# JSON string (e.g., from file or API response)
353
json_data = '''
354
{
355
"name": "Project",
356
"version": "1.0.0",
357
"children": [
358
{
359
"name": "src",
360
"type": "directory",
361
"children": [
362
{"name": "main.py", "type": "file", "size": 1024},
363
{"name": "utils.py", "type": "file", "size": 512}
364
]
365
},
366
{
367
"name": "tests",
368
"type": "directory",
369
"children": [
370
{"name": "test_main.py", "type": "file", "size": 256}
371
]
372
}
373
]
374
}
375
'''
376
377
# Import JSON to tree
378
importer = JsonImporter()
379
root = importer.import_(json_data)
380
381
print(RenderTree(root))
382
# AnyNode(name='Project', version='1.0.0')
383
# ├── AnyNode(name='src', type='directory')
384
# │ ├── AnyNode(name='main.py', size=1024, type='file')
385
# │ └── AnyNode(name='utils.py', size=512, type='file')
386
# └── AnyNode(name='tests', type='directory')
387
# └── AnyNode(name='test_main.py', size=256, type='file')
388
389
# Custom node class
390
class FileNode(Node):
391
def __str__(self):
392
if self.type == "file":
393
return f"{self.name} ({self.size} bytes)"
394
return self.name
395
396
custom_importer = JsonImporter(nodecls=FileNode)
397
custom_root = custom_importer.import_(json_data)
398
print(RenderTree(custom_root))
399
```
400
401
## Advanced Import/Export Patterns
402
403
### Round-trip Conversion
404
405
Export and re-import data to verify integrity:
406
407
```python
408
from anytree import Node, DictExporter, DictImporter, RenderTree
409
410
# Original tree
411
original = Node("root")
412
Node("child1", parent=original, value=42)
413
Node("child2", parent=original, value=24)
414
415
print("Original:")
416
print(RenderTree(original))
417
418
# Export to dict
419
exporter = DictExporter()
420
data = exporter.export(original)
421
422
# Import back from dict
423
importer = DictImporter()
424
restored = importer.import_(data)
425
426
print("\nRestored:")
427
print(RenderTree(restored))
428
429
# Verify integrity
430
def tree_equals(node1, node2):
431
if node1.name != node2.name:
432
return False
433
if len(node1.children) != len(node2.children):
434
return False
435
return all(tree_equals(c1, c2) for c1, c2 in zip(node1.children, node2.children))
436
437
print(f"Trees equal: {tree_equals(original, restored)}")
438
```
439
440
### Custom Attribute Handling
441
442
Control which attributes are exported and how they're processed:
443
444
```python
445
from anytree import Node, DictExporter, DictImporter
446
447
class CustomNode(Node):
448
def __init__(self, name, **kwargs):
449
# Separate public and private attributes
450
super().__init__(name)
451
self.public_data = {}
452
self.private_data = {}
453
454
for key, value in kwargs.items():
455
if key.startswith('_'):
456
self.private_data[key] = value
457
else:
458
self.public_data[key] = value
459
460
# Create tree with mixed attributes
461
root = CustomNode("root", config="public", _secret="private")
462
child = CustomNode("child", parent=root, setting="visible", _internal="hidden")
463
464
# Export only public attributes
465
def public_attrs_only(attrs):
466
return [(k, v) for k, v in attrs if not k.startswith('_') and k != 'private_data']
467
468
exporter = DictExporter(attriter=public_attrs_only)
469
public_data = exporter.export(root)
470
print("Public data only:", public_data)
471
```
472
473
### Batch Processing
474
475
Process multiple trees or large datasets efficiently:
476
477
```python
478
from anytree import Node, JsonExporter, JsonImporter
479
import json
480
481
def process_tree_batch(tree_list, output_file):
482
"""Export multiple trees to single JSON file"""
483
exporter = JsonExporter()
484
batch_data = []
485
486
for tree in tree_list:
487
tree_data = json.loads(exporter.export(tree))
488
batch_data.append(tree_data)
489
490
with open(output_file, 'w') as f:
491
json.dump(batch_data, f, indent=2)
492
493
def import_tree_batch(input_file):
494
"""Import multiple trees from JSON file"""
495
importer = JsonImporter()
496
497
with open(input_file, 'r') as f:
498
batch_data = json.load(f)
499
500
trees = []
501
for tree_data in batch_data:
502
tree_json = json.dumps(tree_data)
503
tree = importer.import_(tree_json)
504
trees.append(tree)
505
506
return trees
507
508
# Example usage
509
trees = [
510
Node("Tree1", children=[Node("A"), Node("B")]),
511
Node("Tree2", children=[Node("X"), Node("Y")]),
512
]
513
514
process_tree_batch(trees, "trees.json")
515
restored_trees = import_tree_batch("trees.json")
516
```
517
518
### Legacy Format Support
519
520
Handle different data formats and schemas:
521
522
```python
523
from anytree import DictImporter, Node
524
525
class LegacyImporter(DictImporter):
526
"""Import from legacy format with different field names"""
527
528
def import_(self, data):
529
# Convert legacy format to standard format
530
converted = self._convert_legacy_format(data)
531
return super().import_(converted)
532
533
def _convert_legacy_format(self, node_data):
534
"""Convert old field names to new format"""
535
if isinstance(node_data, dict):
536
converted = {}
537
538
# Map old field names to new ones
539
field_mapping = {
540
'title': 'name',
541
'subnodes': 'children',
542
'properties': 'attributes'
543
}
544
545
for old_key, value in node_data.items():
546
new_key = field_mapping.get(old_key, old_key)
547
548
if new_key == 'children' and isinstance(value, list):
549
converted[new_key] = [self._convert_legacy_format(child) for child in value]
550
else:
551
converted[new_key] = value
552
553
return converted
554
return node_data
555
556
# Legacy data format
557
legacy_data = {
558
"title": "Old Root",
559
"properties": {"version": "1.0"},
560
"subnodes": [
561
{"title": "Old Child 1", "properties": {"type": "folder"}},
562
{"title": "Old Child 2", "properties": {"type": "file"}}
563
]
564
}
565
566
# Import using legacy converter
567
legacy_importer = LegacyImporter()
568
tree = legacy_importer.import_(legacy_data)
569
print(f"Imported root: {tree.name}") # "Old Root"
570
```
571
572
## Visualization Integration
573
574
### Graphviz Integration
575
576
Generate publication-quality diagrams:
577
578
```python
579
from anytree import Node, DotExporter
580
import subprocess
581
582
def create_org_chart():
583
# Create organizational structure
584
ceo = Node("Alice Johnson", title="CEO", level="C-Level")
585
cto = Node("Bob Smith", parent=ceo, title="CTO", level="C-Level")
586
cfo = Node("Carol Davis", parent=ceo, title="CFO", level="C-Level")
587
588
# Engineering team
589
senior_eng = Node("David Wilson", parent=cto, title="Senior Engineer", level="Senior")
590
junior_eng = Node("Eve Brown", parent=cto, title="Junior Engineer", level="Junior")
591
592
# Finance team
593
accountant = Node("Frank Miller", parent=cfo, title="Accountant", level="Associate")
594
595
return ceo
596
597
def generate_styled_chart(root, filename):
598
def node_attrs(node):
599
attrs = [f'label="{node.name}\\n{node.title}"']
600
601
# Color by level
602
colors = {
603
"C-Level": "lightblue",
604
"Senior": "lightgreen",
605
"Junior": "lightyellow",
606
"Associate": "lightpink"
607
}
608
609
if hasattr(node, 'level'):
610
attrs.append(f'fillcolor={colors.get(node.level, "white")}')
611
attrs.append('style=filled')
612
613
return ", ".join(attrs)
614
615
exporter = DotExporter(root,
616
nodeattrfunc=node_attrs,
617
options=['rankdir=TB', 'node [shape=box]'])
618
619
# Generate PNG
620
exporter.to_picture(f"{filename}.png", format="png", prog="dot")
621
622
# Generate SVG for web use
623
exporter.to_picture(f"{filename}.svg", format="svg", prog="dot")
624
625
# Create and export chart
626
org_root = create_org_chart()
627
generate_styled_chart(org_root, "org_chart")
628
```
629
630
### Web Integration
631
632
Export for web-based visualization:
633
634
```python
635
from anytree import Node, JsonExporter
636
import json
637
638
def create_interactive_data(root):
639
"""Prepare tree data for D3.js or similar libraries"""
640
641
def add_ui_attributes(attrs):
642
# Add attributes needed for interactive visualization
643
enhanced = list(attrs)
644
enhanced.extend([
645
('id', f"node_{hash(attrs)}"),
646
('collapsed', False),
647
('selected', False)
648
])
649
return enhanced
650
651
exporter = JsonExporter(attriter=add_ui_attributes, indent=2)
652
json_data = exporter.export(root)
653
654
# Wrap in format expected by visualization libraries
655
wrapped_data = {
656
"version": "1.0",
657
"tree": json.loads(json_data),
658
"metadata": {
659
"node_count": len(list(root.descendants)) + 1,
660
"max_depth": root.height,
661
"created": "2024-01-01T00:00:00Z"
662
}
663
}
664
665
return json.dumps(wrapped_data, indent=2)
666
667
# Generate interactive data
668
tree_root = Node("Interactive Root")
669
Node("Branch 1", parent=tree_root, category="primary")
670
Node("Branch 2", parent=tree_root, category="secondary")
671
672
interactive_json = create_interactive_data(tree_root)
673
print(interactive_json)
674
```