Data::TreeDumper - Dumps a data structure in a tree fashion.
use Data::TreeDumper ; my $sub = sub {} ; my $s = { A => { a => { } , bbbbbb => $sub , c123 => $sub , d => \$sub } , C => { b => { a => { a => { } , b => sub { } , c => 42 } } } , ARRAY => [qw(elment_1 element_2 element_3)] } ; #------------------------------------------------------------------- # package setup data #------------------------------------------------------------------- $Data::TreeDumper::Useascii = 0 ; $Data::TreeDumper::Maxdepth = 2 ; $Data::TreeDumper::Filter = \&Data::TreeDumper::HashKeysSorter ; print DumpTree($s, 'title') ; print DumpTree($s, 'title', MAX_DEPTH => 1) ; print DumpTrees ( [$s, "title", MAX_DEPTH => 1] , [$s2, "other_title", DISPLAY_ADDRESS => 0] , USE_ASCII => 1 , MAX_DEPTH => 5 ) ;
title: |- A [H1] | |- a [H2] | |- bbbbbb = CODE(0x8139fa0) [C3] | |- c123 [C4 -> C3] | `- d [R5] | `- REF(0x8139fb8) [R5 -> C3] |- ARRAY [A6] | |- 0 [S7] = elment_1 | |- 1 [S8] = element_2 | `- 2 [S9] = element_3 `- C [H10] `- b [H11] `- a [H12] |- a [H13] |- b = CODE(0x81ab130) [C14] `- c [S15] = 42
Data::Dumper and other modules do a great job at dumping data structure but their output sometime takes more brain to understand the dump than it takes to understand the data itself. When dumping big amounts of data, the output is overwhelming and it's difficult to see the relationship between each piece of the dumped data.
Data::TreeDumper dumps data in a trees like fashion hopping for the output to be easier on the beholder's eye and brain. But it might as well be the opposite!
Each node in the tree has a label. The label contains a type and an address . The label is displayed to the right of the entry name within square brackets.
| |- bbbbbb = CODE(0x8139fa0) [C3] | |- c123 [C4 -> C3] | `- d [R5] | `- REF(0x8139fb8) [R5 -> C3]
The addresses are linearly incremented which should make it easier to locate data. If the entry is a reference to data already displayed, a -> followed with the address of the already displayed data is appended within the label.
ex: c123 [C4 -> C3] ^ ^ | | address of the data refered to | | current address
H: Hash, C: Code, A: Array, R: Reference,
O: Object, S: Scalar, RS: Scalar reference.
No structure is displayed for empty hashes or arrays, The address contains the type.
|- A [S10] = string |- EMPTY_ARRAY [A11] |- B [S12] = 123
Data::TreeDumper has configuration options you can set to modify the output it generates. How to set the options depends on which Interface you use and is explained bellow. The configuration options are available in all the Interfaces and are the Native interface arguments.
The package and object oriented interface take overrides as trailing arguments. Those overrides are active within the current dump call only.
ex: $Data::TreeDumper::Maxdepth = 2 ; # maximum depth set to 1 for the duration of the call only print DumpTree($s, 'title', MAX_DEPTH => 1) ; # maximum depth is 2 print DumpTree($s, 'title') ;
By default, Data::TreeDumper doesn't display the address of the root.
DISPLAY_ROOT_ADDRESS => 1 # show the root address
When the dumped data are not self referential, displaying the address of each node clutters the display. You can direct Data::TreeDumper to not display the node address by using:
DISPLAY_ADDRESS => 0
Data::TreeDumper displays the package an object is blessed in You can direct Data::TreeDumper to not display the package by using:
DISPLAY_OBJECT_TYPE => 1
Setting one of the options bellow will show internal perl data
Cells: <2234> HASH(0x814F20c) |- A1 [H1] <204> HASH(0x824620c) | `- VALUE [S2] = datadatadatadatadatadatadatadatadatadata <85> |- A8 [H11] <165> HASH(0x8243d68) | `- VALUE [S12] = C <46> `- C2 [H19] <165> HASH(0x8243dc0) `- VALUE [S20] = B <46>
Setting this option will show the perl-address of the dumped data.
DISPLAY_PERL_ADDRESS => 1
Setting this option will show the memory allocated size for each element in the tree within angle brackets.
DISPLAY_PERL_SIZE => 1
See also the excellent Devel::Size::Report from which I Stole the idea.
No output will be generated by Data::TreeDumper. Useful when you want to iterate through your data structures and display the data yourself or manipulate the data structure or do a search (see "using filter as iterators" bellow)
Data::TreeDumper can sort the tree nodes with a user defined sub.
FILTER => \&ReverseSort FILTER => \&Data::TreeDumper::HashKeysSorter
The filter sub is passed these arguments:
The filter returns the node's type, an eventual new structure (see bellow) and a list of 'keys' to display. The keys are hash keys or array indexes.
In Perl:
($tree_type, $replacement_tree, @nodes_to_display) = $your_filter->($tree, $level, $path, $nodes_to_display, $setup) ;
Filter are not as complicated as they sound and they are very powerfull, specially when using the path argument. The path idea was given to me by another module writter but I forgot who. Remind me of you so I give you deserved credit.
Lots of examples can be found in filters.pl and I'll be glad to help if you want to develop a filter.
Entries can be removed from the display by not returning their keys.
my $s = {visible => '', also_visible => '', not_visible => ''} ; my $OnlyVisible = sub { my $s = shift ; if('HASH' eq ref $s) { return('HASH', undef, grep {! /^not_visible/} keys %$s) ; } return(Data::TreeDumper::DefaultNodesToDisplay($s)) ; } DumpTree($s, 'title', FILTER => $OnlyVisible) ;
The label for a hash keys or an array index can be altered. This can be used to add visual information to the tree dump. Instead for returning the key name, return an array reference containing the key name and the label you want to display. You only need to return such a reference for the entries you want to change thus a mix of scalars and array ref is acceptable.
sub StarOnA { # hash entries matching /^a/i have '*' prepended my $tree = shift ; if('HASH' eq ref $tree) { my @keys_to_dump ; for my $key_name (keys %$tree) { if($key_name =~ /^a/i) { $key_name = [$key_name, "* $key_name"] ; } push @keys_to_dump, $key_name ; } return ('HASH', undef, @keys_to_dump) ; } return (Data::TreeDumper::DefaultNodesToDisplay($tree)) ; } print DumpTree($s, "Entries matching /^a/i have '*' prepended", FILTER => \&StarOnA) ;
If you use an ansi terminal, you can also change the color of the label, this can greatly improve visual search time. See the label coloring example in colors.pl.
It is possible to replace the whole data structure in a filter. This comes handy when you want to display a "worked" version of the structure. You can even change the type of the data structure, for example changing an array to a hash.
sub ReplaceArray { # replace arrays with hashes!!! my $tree = shift ; if('ARRAY' eq ref $tree) { my $multiplication = $tree->[0] * $tree->[1] ; my $replacement = {MULTIPLICATION => $multiplication} ; return('HASH', $replacement, keys %$replacement) ; } return (Data::TreeDumper::DefaultNodesToDisplay($tree)) ; } print DumpTree($s, 'replace arrays with hashes!', FILTER => \&ReplaceArray) ;
Here is a real life replacement. Tree::Simple http://search.cpan.org/dist/Tree-Simple/ allows one to build tree structures. The child nodes are not directly in the parent object (hash). Here is an unfiltered dump of a tree with seven nodes:
Tree::Simple through Data::TreeDumper |- _children | |- 0 | | |- _children | | | `- 0 | | | |- _children | | | |- _depth = 1 | | | |- _node = 1.1 | | | `- _parent | | |- _depth = 0 | | |- _node = 1 | | `- _parent | |- 1 | | |- _children | | | |- 0 | | | | |- _children | | | | |- _depth = 1 | | | | |- _node = 2.1 | | | | `- _parent | | | |- 1 | | | | |- _children | | | | |- _depth = 1 | | | | |- _node = 2.1a | | | | `- _parent | | | `- 2 | | | |- _children | | | |- _depth = 1 | | | |- _node = 2.2 | | | `- _parent | | |- _depth = 0 | | |- _node = 2 | | `- _parent | `- 2 | |- _children | |- _depth = 0 | |- _node = 3 | `- _parent |- _depth = -1 |- _node = 0 `- _parent = root
This is nice for the developer but not for a user wanting to over see the node hierarchy. One of the possible filters would be:
FILTER => sub { my $s = shift ; if('Tree::Simple' eq ref $s) { my $counter = 0 ; return ( 'ARRAY' , $s->{_children} , map{[$counter++, $_->{_node}]} @{$s->{_children}} # index generation ) ; } return(Data::TreeDumper::DefaultNodesToDisplay($s)) ; }
Which would give this much more readable output:
Tree::Simple through Data::TreeDumper2 |- 1 | `- 1.1 |- 2 | |- 2.1 | |- 2.1a | `- 2.2 `- 3
What about counting the children nodes? The index generating code becomes:
map{[$counter++, "$_->{_node} [" . @{$_->{_children}} . "]"]} @{$s->{_children}} Tree::Simple through Data::TreeDumper4 |- 1 [1] | `- 1.1 [0] |- 2 [3] | |- 2.1 [0] | |- 2.1a [0] | `- 2.2 [0] `- 3 [0]
It is possible to chain filters. CreateChainingFilter takes a list of filtering sub references. The filters must properly handle the third parameter passed to them.
Suppose you want to chain a filter, that adds a star before each hash key label, with a filter that removes all (original) keys that match /^a/i.
sub AddStar { my $s = shift ; my $level = shift ; my $path = shift ; my $keys = shift ; if('HASH' eq ref $s) { $keys = [keys %$s] unless defined $keys ; my @new_keys ; for (@$keys) { if('' eq ref $_) { push @new_keys, [$_, "* $_"] ; } else { # another filter has changed the label push @new_keys, [$_->[0], "* $_->[1]"] ; } } return('HASH', undef, @new_keys) ; } return(Data::TreeDumper::DefaultNodesToDisplay($s)) ; } ; sub RemoveA { my $s = shift ; my $level = shift ; my $path = shift ; my $keys = shift ; if('HASH' eq ref $s) { $keys = [keys %$s] unless defined $keys ; my @new_keys ; for (@$keys) { if('' eq ref $_) { push @new_keys, $_ unless /^a/i ; } else { # another filter has changed the label push @new_keys, $_ unless $_->[0] =~ /^a/i ; } } return('HASH', undef, @new_keys) ; } return(Data::TreeDumper::DefaultNodesToDisplay($s)) ; } ; DumpTree($s, 'Chained filters', FILTER => CreateChainingFilter(\&AddStar, \&RemoveA)) ;
It is possible to define one filter for a specific level. If a filter for a specific level exists it is used instead for the global filter.
LEVEL_FILTERS => {1 => \&FilterForLevelOne, 5 => \&FilterForLevelFive ... } ;
you can iterate in your data structures and display data yoursel, manipulate the data structure or do a search. While iterating the data structure, you can prune the branches that present no interest to speedup the iterations
# this example counts the nodes in a tree (hash based) # a node is counted if it has a '__NAME' key # any field that starts with '__' is considered rivate and we prune so we don't recurse in it # anything that is not a hash (the part of the tree that interests us in this case) is pruned my $number_of_nodes_in_the_dependency_tree = 0 ; my $node_counter = sub { my $tree = shift ; if('HASH' eq ref $tree && exists $tree->{__NAME}) { $number_of_nodes_in_the_dependency_tree++ if($tree->{__NAME} !~ /^__/) ; return('HASH', $tree, grep {! /^__/} keys %$tree) ; # prune to run faster } else { return('SCALAR', 1) ; # prune } } ; DumpTree($dependency_tree, '', NO_OUTPUT => 1, FILTER => $node_counter) ;
This configuration option controls whether the tree trunk is displayed or not.
START_LEVEL => 1:
$tree: |- A [H1] | |- a [H2] | |- bbbbbb = CODE(0x8139fa0) [C3] | |- c123 [C4 -> C3] | `- d [R5] | `- REF(0x8139fb8) [R5 -> C3] |- ARRAY [A6] | |- 0 [S7] = elment_1 | |- 1 [S8] = element_2
START_LEVEL => 0:
$tree: A [H1] |- a [H2] |- bbbbbb = CODE(0x8139fa0) [C3] |- c123 [C4 -> C3] `- d [R5] `- REF(0x8139fb8) [R5 -> C3] ARRAY [A6] |- 0 [S7] = elment_1 |- 1 [S8] = element_2
You can direct Data:TreeDumper to output ANSI codes instead for ASCII characters. The display will be much nicer but takes slightly longer time (not significant for small data structures).
USE_ASCII => 0 # will use ANSI codes instead
Controls the depth beyond which which we don't recurse into a structure. Default is -1, which means there is no maximum depth. This is useful to limit the amount of data displayed.
MAX_DEPTH => 1
Every line of the tree dump will be appended with the value of INDENTATION.
INDENTATION => ' ' ;
Data:TreeDumper can prepend the level of the current line to the tree glyphs. This can be very useful when searching in tree dump either visually or with a pager.
NUMBER_LEVELS => 2 NUMBER_LEVELS => \&NumberingSub
NUMBER_LEVELS can be assigned a number or a sub reference. When assigned a number, Data::TreeDumper will use that value to define the width of the field where the level is displayed. For more control, you can define a sub that returns a string to be displayed on the left side of the tree glyphs. The example bellow tags all the nodes which level is zero.
print DumpTree($s, "Level numbering", NUMBER_LEVELS => 2) ; sub GetLevelTagger { my $level_to_tag = shift ; sub { my ($element, $level, $setup) = @_ ; my $tag = "Level $level_to_tag => "; if($level == 0) { return($tag) ; } else { return(' ' x length($tag)) ; } } ; } print DumpTree($s, "Level tagging", NUMBER_LEVELS => GetLevelTagger(0)) ;
Another way to enhance the output for easier searching is to colorize it. Data::TreeDumper can colorize the glyph elements or whole levels. If your terminal supports ANSI codes, using Term::ANSIColors and Data::TreeDumper together can greatly ease the reading of large dumps. See the examples in color.pl.
COLOR_LEVELS => [\@color_codes, $reset_code]
When passed an array reference, the first element is an array containing coloring codes. The codes are indexed with the node level modulo the size of the array. The second element is used to reset the color after the glyph is displayed. If the second element is an empty string, the glyph and the rest of the level is colorized.
COLOR_LEVELS => \&LevelColoringSub
If COLOR_LEVEL is assigned a sub, the sub is called for each glyph element. It is passed the following elements:
It should return a coloring code and a reset code. If you return an empty string for the reset code, the whole node is displayed using the last glyph element color.
If level numbering is on, it is also colorized.
Data::TreeDumper uses the Text::Wrap module to wrap your data to fit your display. Entries can be wrapped multiple times so they snuggly fit your screen.
| | |- 1 [S21] = 1 | | `- 2 [S22] = 2 | `- 3 [O23 -> R17] |- ARRAY_ZERO [A24] |- B [S25] = scalar |- Long_name Long_name Long_name Long_name Long_name Long_name | Long_name Long_name Long_name Long_name Long_name Long_name | Long_name Long_name Long_name Long_name Long_name [S26] = 0
Data::TreeDumper had a plug-in interface for other rendering format. The renderer callbacks are set by overriding the native renderer. Thanks to Stevan Little author of Tree::Simple::View for getting Data::TreeDumper on this track.
DumpTree ( $s , 'Tree' , RENDERER => { BEGIN => \&RenderDhtmlBegin , NODE => \&RenderDhtmlNode , END => \&RenderDhtmlEnd # data needed by the renderer , PREVIOUS_LEVEL => -1 , PREVIOUS_ADDRESS => 'ROOT' } ) ;
{RENDERER}{BEGIN} is called before the traversal of the data structure starts. This allows you to setup the document (ex:: html header).
{RENDERER}{NODE} is called for each node in the data structure. The following arguments are passed to the callback
{RENDERER}{END} is called after the last node has been processed.
{RENDERER}{ ... }Arguments to the renderer can be stores within the {RENDERER} hash.
Renderers should be defined in modules under Data::TreeDumper::Renderer and should define a function called GetRenderer. GetRenderer can be passed whatever arguments the renderer's developer whishes. It is also acceptable for the modules to also export a specifc sub.
print DumpTree($s, 'Tree', Data::TreeDumper::Renderer::DHTML::GetRenderer()) ; or print DumpTree($s, 'Tree', GetDhtmlRenderer()) ;
if {RENDERER} is set to a scalar, Data::TreeDumper will load the specified module if it exists. GetRenderer will be called without arguments.
print DumpTree($s, 'Tree', RENDERER => 'DHTML') ;
if {RENDERER}{NAME} is set to a scalar, Data::TreeDumper will load the specified module if it exists. GetRenderer will be called without arguments. Arguments to the renderer can aither be passed to the GetRenderer sub or as elements in the {RENDERER} hash.
print DumpTree($s, 'Tree', RENDERER => {NAME => 'DHTML', STYLE => \$style) ;
When no console exists, while redirecting to a file for example, Data::TreeDumper uses the variable VIRTUAL_WIDTH instead. Default is 120.
VIRTUAL_WIDTH => 120 ;
COLOR_LEVELS
DISPLAY_ADDRESS
DISPLAY_PERL_SIZE
DISPLAY_ROOT_ADDRESS
DISPLAY_PERL_ADDRESS
FILTER
INDENTATION
LEVEL_FILTERS
MAX_DEPTH
NUMBER_LEVELS
START_LEVEL
USE_ASCII
VIRTUAL_WIDTH
NO_OUTPUT
$Data::TreeDumper::Startlevel = 1 ; $Data::TreeDumper::Useascii = 1 ; $Data::TreeDumper::Maxdepth = -1 ; $Data::TreeDumper::Indentation = '' ; $Data::TreeDumper::Virtualwidth = 120 ; $Data::TreeDumper::Displayrootaddress = 0 ; $Data::TreeDumper::Displayaddress = 1 ; $Data::TreeDumper::DisplayObjectType = 1 ; $Data::TreeDumper::Displayperlsize = 0 ; $Data::TreeDumper::Displayperladdress = 0 ; $Data::TreeDumper::Filter = \&FlipEverySecondOne ; $Data::TreeDumper::Levelfilters = {1 => \&Filter_1, 5 => \&Filter_5} ; $Data::TreeDumper::Numberlevels = 0 ; $Data::TreeDumper::Colorlevels = undef ; $Data::TreeDumper::Nooutput = 0 ; # generate an output
DumpTree uses the configuration variables defined above. It takes the following arguments
print DumpTree($s, "title", MAX_DEPTH => 1) ;
DumpTrees uses the configuration variables defined above. It takes the following arguments
print DumpTrees ( [$s, "title", MAX_DEPTH => 1] , [$s2, "other_title", DISPLAY_ADDRESS => 0] , USE_ASCII => 1 , MAX_DEPTH => 5 ) ;
None I know of in this release but plenty, lurking in the dark corners, waiting to be found.
Four examples files are included in the distribution.
usage.pl shows you how you can use Data::TreeDumper.
filters.pl shows you how you how to do advance filtering.
colors.pl shows you how you how to colorize a dump.
try_it.pl is meant as a scratch pad for you to try Data::TreeDumper.
Optional Devel::Size if you want Data::TreeDumper to show perl sizes for the tree elements.
DumpTree, DumpTrees and CreateChainingFilter.
Khemir Nadim ibn Hamouda. <nadim@khemir.net>
Thanks to Ed Avis for showing interest and pushing me to re-write the documentation.
Copyright (c) 2003 Nadim Ibn Hamouda el Khemir. All rights reserved. This program is free software; you can redis- tribute it and/or modify it under the same terms as Perl itself.
If you find any value in this module, mail me! All hints, tips, flames and wishes are welcome at <nadim@khemir.net>.
Data::TreeDumper::00. Data::Dumper. Devel::Size::Report. Devel::Size.
PBS: the Perl Build System from which Data::TreeDumper was extracted. Contact the author for more information about PBS.
1 POD Error
The following errors were encountered while parsing the POD:
Non-ASCII character seen before =encoding in '(à'. Assuming CP1252
To install Data::TreeDumper, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Data::TreeDumper
CPAN shell
perl -MCPAN -e shell install Data::TreeDumper
For more information on module installation, please visit the detailed CPAN module installation guide.