Data::TreeDraw - Graphical representation of nested data structures.
This document describes Data::TreeDraw version 0.0.5
I noticed that object in some classes that use overloading killed the program. So now the program copies/strips the raw data from a class (nested or root) before continuing. This way it should now handle objects from any class.
While fixing the above problem I ended up extending the notation option so that it works with the the unwrap_objects option. You can now get the appropriate notation within heavily nested object references.
unwrap_objects
While this module was written for me to visualise the internal structure of Perl5 Objects I was developing it should serve for any data-structure where you need to quickly analyse, understand and check the internal structure and values and more importantly access it - see "USEFUL EXAMPLE".
While there are a number of great programs out there for Dumping and visualising heavily-nested and data-rich data-structures these can often be overwhelming and hard to read - this modules aims to address these issues by not only giving a very simple interface for drawing clear branching structures but also a number of features that allow data-rich features e.g. long Lists and List-of-Lists to be printed more naturally and succinctly for interpretation - see "Long Arrays" and "Lists-of-Lists" in "OVERVIEW" (See "OVERVIEW" and "OPTIONS" for a comprehensive list of features).
Even more tricky than interpreting data-rich structures in heavily nested references can be the process of finding the exact combination of array-elements and hash-keys to use to dereference/access a particular ARRAY ref, SCALAR value etc. - often requiring that you manually back-trace over a dumped structure to find the specific combination to use. The notation option of this module (defaults to on - see "notation" in "OVERVIEW").
notation
Additionally, the output may be restricted in many ways including: printing only branches within the data-structure that match a specific hash key value (see "HASH key lookup" in "OVERVIEW"), printing only those SCALAR values matching a specific string value (see "SCALAR value lookup" in "OVERVIEW"), print only branches with internal nesting levels higher or lower than a specific level (see "Maximum printing depth" and "Minimum printing depth" in "OVERVIEW").
Alternatively you may add to the output: If you have object references within your nested structure but want the tree branching to carry on recursing into them so as to see their internals use the unwrap_object option (see "Object recursion" in "OVERVIEW"). If you want a object method introspection as implement by the Class::MOP module use the object_methods option (see "Method introspection for objects" in "OVERVIEW").
unwrap_object
object_methods
This module was written by me, for me, and so internally may be a bit esoteric. If there is significant interest I will improve and expand it.
Create suitable structure.
# Create a Code Reference. my $c_ref = sub { print q{blah}; $_->[0] }; # Create a heavily nested set of references. my $r_ref = \\\\\[q{pink},321]; # Create an object of type HASH; my $pca->{g} = 4; bless $pca, q{Some::Class}; # Create a GLOB. *f = *g; # Create a nested data structure with various typed of nested data e.g. defined and undefined SCALAR, ARRAY, HASH and REFs my $a = [ q{hi}, q{there}, [ q{i}, q{am} ], 1, { r => \\q{} }, { r => { y => 3 }, t => [ { r => *g } ] }, $c_ref, *f, $r_ref, q{nine}, [ [ 3,q{----3-----}, 3 ], [3, 3, 3, 3, ], [ 3, 3, 3, ] ], 1, \\\{ r => \[] }, \\\q{foo}, { g => 2, t => 2, y => q{------2-------}, r => [3], step => { the => q{blah} }, o => [ [4] ] }, [ [ [q{--4--}]] , [ [ 4, 4, q{-4-}, q{--4--} ] ], 2, [3] ], \\\q{}, 1, [ [3 , 3], 2, $pca] , 1, { g => [ 3, 3, q{blah}, 3, 3, 3 ] } ,[ [ 3 ] ], $a, undef ]; # Create cyclic references. my $b = [$a]; my $c = [$b]; $a->[2][3] = $c; $a->[2][2] = $a; # Make our nested data structure an object bless $a, q{Other::Class};
Use module and call draw routine on structure.
draw
use Data::TreeDraw; draw($a);
Prints:
Method called from Package 'main' on Blessed Object of type 'ARRAY' and Class 'Other::Class'. ARRAY REFERENCE (0) | |__SCALAR = 'hi' (1) [ '->[0]' ] | |__SCALAR = 'there' (1) [ '->[1]' ] | |__ARRAY REFERENCE (1) [ '->[2]' ] | | | |__SCALAR = 'i' (2) [ '->[2][0]' ] | | | |__SCALAR = 'am' (2) [ '->[2][1]' ] | | | |__CYCLIC REFERENCE (2) [ '->[2][2]' ] | | | |__ARRAY REFERENCE (2) [ '->[2][3]' ] | | | |__ARRAY REFERENCE (3) [ '->[2][3][0]' ] | | | |__CYCLIC REFERENCE (4) [ '->[2][3][0][0]' ] | |__SCALAR = '1' (1) [ '->[3]' ] | |__HASH REFERENCE (1) [ '->[4]' ] | | | |__'r'=>REFERENCE-TO-REFERENCE (2) | | | |__SCALAR REFERENCE (3) | | | |__SCALAR = '' [EMPTY STRING] (4) | |__HASH REFERENCE (1) [ '->[5]' ] | | | |__'r'=>HASH REFERENCE (2) [ '->[5]{r}' ] | | | | | |__'y'=>SCALAR = '3' (3) [ '->[5]{r}{y}' ] | | | |__'t'=>ARRAY REFERENCE (2) [ '->[5]{t}' ] | | | |__HASH REFERENCE (3) [ '->[5]{t}[0]' ] | | | |__'r'=>GLOB = '*main::g' (4) [ '->[5]{t}[0]{r}' ] | etc.
A simple example to demonstrate some of the features of this module is giving with a simple database lookup using DBI. We want to extract usernames, passwords, addresses and email addresses of all the entries within a table in a single ARRAY reference and modify it.
use DBI; # connect to DB etc. my $sql = q{select username, password, address, email from some_table}; my $db_as_a_ref = $dbh->selectall_hashref($sql);
By calling the program on the generated ARRAY reference we can look at the structure:
draw($db_as_h_ref);
This prints something like:
ARRAY REFERENCE (0) | |__ARRAY REFERENCE (1) [ '->[0]' ] | | | |__SCALAR = '1' (2) [ '->[0][0]' ] | | | |__SCALAR = 'user0' (2) [ '->[0][1]' ] | | | |__SCALAR = 'password0' (2) [ '->[0][2]' ] | | | |__SCALAR = 'address' (2) [ '->[0][3]' ] | | | |__SCALAR = 'user0@blah.net' (2) [ '->[0][4]' ] | lots more entries... | |__ARRAY REFERENCE (1) [ '->[13]' ] | | | |__SCALAR = '14' (2) [ '->[13][0]' ] | | | |__SCALAR = 'Dan' (2) [ '->[13][1]' ] | | | |__SCALAR = 'Not telling' (2) [ '->[13][2]' ] | | | |__SCALAR = 'Rio de Janeiro, Brasil' (2) [ '->[13][3]' ] | | | |__SCALAR = 'dsth@cpan.net' (2) [ '->[13][4]' ] | lots more entries...
First we immediately see the internal structure of the entries - namely that the passes reference was an ARRAY reference and that each individual entry is simple another nested ARRAY reference directly within this top level ARRAY reference (i.e. the nesting level of every element is given in parenthesis to the side of each entry. Next, we scroll down to my entry (shown by SCALAR value 'Dan' with nesting level 2 within one of these nested ARRAY references at nesting level 1) and see my address ('Rio de Janeiro, Brasil'. I need to change my address within this structure to 'NY, USA'. To do this I simply append the arrow operator dereferencing notation given within the square brackets to the right of the entry. Thus to change my address I immediately know that I need to use ->[13][3] dereferencing notation. Thus we change my address:
$db_as_h_ref->->[13][3] = q{NY, USA};
Perhaps I didn't want all the other information in the data-structure as I just want to change my name. We use the scalar_val option.
scalar_val
draw($db_as_h_ref, { scalar_val => 'Dan' });
This prints just:
SCALAR value 'Dan' found at indentation level '2': | |__SCALAR = 'Dan' (2) [ '->[13][1]' ] | | SCALAR value 'Dan' found 1 times in nested data structure.
So we immediately change my name:
$db_as_h_ref->[13][1] = q{Daniel};
Instead of passing the database entries as an ARRAY reference we may have used a HASH reference:
my $sql = q{select username, password, address, email from some_table}; my $db_as_h_ref = $dbh->selectall_hashref($sql, q{username});
In this case when we use the basic draw routine we obtain:
HASH REFERENCE (0) | |__'user33'=>HASH REFERENCE (1) [ '->{user33}' ] | | | |__'email'=>SCALAR = 'user33@blah.net' (2) [ '->{user33}{email}' ] | | | |__'password'=>SCALAR = 'password33' (2) [ '->{user33}{password}' ] | | | |__'username'=>SCALAR = 'user33' (2) [ '->{user33}{username}' ] | lots more entries... | |__'Dan'=>HASH REFERENCE (1) [ '->{Dan}' ] | |__'email'=>SCALAR = 'dsth@cpan.net' (2) [ '->{Dan}{email}' ] | |__'password'=>SCALAR = 'Not telling' (2) [ '->{Dan}{password}' ] | |__'username'=>SCALAR = 'Dan' (2) [ '->{Dan}{username}'
First, the termination of the basic tree root descending from the passed HASH reference (with nesting level 0) shows that it´s the last of the entries in the structure. Again we immediately see the dereferencing notation we need to append to the passed structure. However, I really only wanted to see the entry corresponding to my details so we use the hash_key option:
hash_key
draw($db_as_h_ref, { hash_key => 'Dan' });
This simply prints:
HASH key 'Dan' found at indentation level '1': |__'Dan'=>HASH REFERENCE (1) [ '->{Dan}' ] | |__'email'=>SCALAR = 'dsth@cpan.net' (2) [ '->{Dan}{email}' ] | |__'password'=>SCALAR = 'Not telling' (2) [ '->{Dan}{password}' ] | |__'username'=>SCALAR = 'Dan' (2) [ '->{Dan}{username}' ] HASH key 'Dan' found 1 times in nested data structure.
The module exports a single sub-routine call draw. Simply call this routine with the data structure you wish to print along with a HASH reference of any options you wish to pass - see "OPTIONS" and this section.
All structures are displayed as a "clear" Tree-structure branching from a single root.
ARRAY REFERENCE (0) | |__SCALAR = 'hi' (1) [ '->[0]' ] | |__SCALAR = 'there' (1) [ '->[1]' ] | |__ARRAY REFERENCE (1) [ '->[2]' ] | | | |__SCALAR = 'i' (2) [ '->[2][0]' ] | | | |__SCALAR = 'am' (2) [ '->[2][1]' ] | | | |__CYCLIC REFERENCE (2) [ '->[2][2]' ] | | | |__ARRAY REFERENCE (2) [ '->[2][3]' ] | | | |__ARRAY REFERENCE (3) [ '->[2][3][0]' ] etc.
The notation option (defaults to on - with "1") results in the printing along side (in square-brackets) the specific REFERENCE or SCALAR value of the particular arrow-notation required to access/dereference that REFERENCE or SCALAR value e.g. "[ '->[4]{hash_key}[12]' ]" next to an ARRAY reference means that to dereference that particular value within the passed structure simply append "->[4]{hash_key}[12] " to the originally passed reference.
e.g. To access the above ARRAY reference printed as: "ARRAY REFERENCE (2) [ '->[2][3][0]' ]" Simply use: $original_data_passed->[2][3][0];
For a more compressed version of the print disable the spaces options by setting it to "0" (this option is enabled by default). The above Tree is printed as:
spaces
ARRAY REFERENCE (0) |__SCALAR = 'hi' (1) [ '->[0]' ] |__SCALAR = 'there' (1) [ '->[1]' ] |__ARRAY REFERENCE (1) [ '->[2]' ] | |__SCALAR = 'i' (2) [ '->[2][0]' ] | |__SCALAR = 'am' (2) [ '->[2][1]' ] | |__CYCLIC REFERENCE (2) [ '->[2][2]' ] | |__ARRAY REFERENCE (2) [ '->[2][3]' ] | |__ARRAY REFERENCE (3) [ '->[2][3][0]' ] | |__CYCLIC REFERENCE (4) [ '->[2][3][0][0]' ] etc.
The nesting/indentation level of ALL structures is printed along-side of the REFERENCE/SCALAR value in parenthesis:
e.g. SCALAR = 'some_value' (nesting_level/e.g.4)
Any SCALAR value containing an empty string is printed as that e.g. '', but to ease distinguishing it from ' ' it additionally prints [EMPTY STRING] along-side.
e.g. SCALAR = '' [EMPTY STRING]
With data-rich structures arrays may often have many elements. In such cases printing each SCALAR value within the array on a separate line makes reading the structure difficult:
ARRAY REFERENCE (2) [ '->[20]{g}' ] | |__SCALAR = 'val1' (3) [ '->[20]{g}[0]' ] | |__SCALAR = 'val2' (3) [ '->[20]{g}[1]' ] | |__SCALAR = 'val3' (3) [ '->[20]{g}[2]' ] | |__SCALAR = 'val4' (3) [ '->[20]{g}[3]' ] etc.
The long_array option (defaults to off - "0") over-rides this behaviour and instead arrays consisting of "just" SCALAR values are printed on a single-line. With this setting relatively short arrays are printed in full on a single line along with their length:
long_array
ARRAY REFERENCE (3) ---LONG_LIST_OF_SCALARS--- [ length = 4 ]: val1, val2, val3, val4 [ '->[20]{g}'
Longer arrays are printed in a similar fashion except that only the length and first 3 elements are printed (just to indicate the nature of the values stored by the array).
ARRAY REFERENCE (2) ---LONG_LIST_OF_SCALARS--- [ length = 4 ] e.g. 0..2: val1, val2, val3 [ '->[20]{g}' ]
You can switch the length of array that triggers these two behaviours using the array_length option (defaults to 3). See OPTIONS for further info.
array_length
In cases of Lists-of-lists the readability may suffer further - especially as these structures often correspond to 2-dim tables. Thus in cases of ARRAYS consisting uniquely of ARRAYS of SCALARS:
|__ARRAY REFERENCE (2) [ '->[10][0]' ] | | | |__SCALAR = '2' (3) [ '->[10][0][0]' ] | | | |__SCALAR = '3' (3) [ '->[10][0][1]' ] | | | |__SCALAR = '3' (3) [ '->[10][0][2]' ] | |__ARRAY REFERENCE (2) [ '->[10][1]' ] | | | |__SCALAR = '3' (3) [ '->[10][1][0]' ] | | | |__SCALAR = '3' (3) [ '->[10][1][1]' ] | | | |__SCALAR = '3' (3) [ '->[10][1][2]' ] | | | |__SCALAR = '3' (3) [ '->[10][1][3]' ] | |__ARRAY REFERENCE (2) [ '->[10][2]' ] | |__SCALAR = '3' (3) [ '->[10][2][0]' ] | |__SCALAR = '3' (3) [ '->[10][2][1]' ] | |__SCALAR = '3' (3) [ '->[10][2][2]' ]
The lol option when set to "1" will replace these structures with (this option defaults to "0"):
lol
ARRAY REFERENCE (1) ---LIST OF LISTS--- [ rows = 3 and longest nested list length = 4 ] [ '->[10]' ]
You may be interested in the particular values within these structures. In this case lol set to "2" creates a temporary break in the tree-structure with a table of the values and their access values and an extension of their root:
ARRAY REFERENCE (1) ---LIST OF LISTS--- [ rows = 3 and longest nested list length = 4 ] [ '->[10]' ] | --- .----------+-------+-------+-------+-------. | | ..[0] | ..[1] | ..[2] | ..[3] | +----------+-------+-------+-------+-------+ | ..[0].. | '3' | '3' | '3' | --- | | ..[1].. | '3' | '3' | '3' | '3' | | ..[2].. | '3' | '3' | '3' | --- | '----------+-------+-------+-------+-------' --- |
This option may be used with the long_arrays option in which case the lol option takes precedence and above structure would be printed as above instead of:
long_arrays
|__ARRAY REFERENCE (1) [ '->[10]' ] | |__ARRAY REFERENCE (2) ---LONG_LIST_OF_SCALARS--- [ length = 3 ]: 3, 3, 3 [ '->[10][0]' ] | |__ARRAY REFERENCE (2) ---LONG_LIST_OF_SCALARS--- [ length = 4 ]: 3, 3, 3, 3 [ '->[10][1]' ] | |__ARRAY REFERENCE (2) ---LONG_LIST_OF_SCALARS--- [ length = 3 ]: 3, 3, 3 [ '->[10][2]' ]
You may just be interested in those parts of the structure with specific SCALAR values. In this case use the scalar_val option. This will only print parts of the branching structure where SCALARS are encountered with a particular string value. See "scalar_val" in "OPTIONS" for usage.
SCALAR value 'blah' found at indentation level '3': | | |__'the'=>SCALAR = 'blah' (3) [ '->[14]{step}{the}' ] | | SCALAR value 'blah' found at indentation level '3': | |__SCALAR = 'blah' (3) [ '->[20]{g}[2]' ] | | SCALAR value 'blah' found 2 times in nested data structure.
As HASHES are simply unordered LISTs using a look up key HASH references are displayed just ARRAY references only with the hash key appended e.g.
|__HASH REFERENCE (1) [ '->[20]' ] | |__'hash_key'=>ARRAY REFERENCE (2) [ '->[20]{g}' ] | |__SCALAR = '3' (3) [ '->[20]{g}[0]' ]
You may just be interested in the values of a particular HASH entry. In this case using the hash_key option you can start Tree printing from when that particular HASH key is encountered. See "hash_key" in "OPTIONS" for usage.
HASH key 'given_key' found at indentation level '5': |__'given_key'=>REFERENCE-TO-REFERENCE (5) | | |__UNDEFINED ARRAY REFERENCE (6) etc. HASH key 'given_key' found 2 times in nested data structure.
You may not be interested in values near the root of the structure. In which case you can set the min_depth option (defaults to 0).
min_depth
Starting print at depth 2. | |__SCALAR = 'i' (2) [ '->[2][0]' ] | | | |__SCALAR = 'am' (2) [ '->[2][1]' ] | | | |__CYCLIC REFERENCE (2) [ '->[2][2]' ] | | | |__ARRAY REFERENCE (2) [ '->[2][3]' ] | | | |__ARRAY REFERENCE (3) [ '->[2][3][0]' ] | | | |__CYCLIC REFERENCE (4) [ '->[2][3][0][0]' ] | Indent decrementing to '1' below min_depth level of '2' | |__'r'=>REFERENCE-TO-REFERENCE (2) | | | |__SCALAR REFERENCE (3) | | | |__SCALAR = '' [EMPTY STRING] (4) |
If you do not wish to view deeply nested structures you can set the max_depth option (defaults to 10):
max_depth
ARRAY REFERENCE (0) | |__SCALAR = 'hi' (1) [ '->[0]' ] | |__SCALAR = 'there' (1) [ '->[1]' ] | |__ARRAY REFERENCE (1) [ '->[2]' ] | | | |__SCALAR EXCEEDS MAX NESTING DEPTH (2) | | | |__SCALAR EXCEEDS MAX NESTING DEPTH (2) | | etc.
In cases where an object reference is pointed within the structure its class will be printed:
|__BLESSED OBJECT BELONGING TO CLASS: Statistics::PCA (2) [ '->[18][2]' ]
However, you may be wish to continue the recursion into the object. This can be done by setting the unwrap_object option to "1" (defaults to "0"):
| |__BLESSED OBJECT BELONGING TO CLASS: Statistics::PCA (3) ---RECURSING-INTO-OBJECT--- | | |__HASH REFERENCE (3) | etc.
Note: while the structure is indented further - the actual indentation level in parenthesis does not change - this is just aids the identification of the type of data-type of the object within the structure. Also as yet, the notation option is not supported with the object_unwrap option.
object_unwrap
You may additionally wish to introspect either the root structure or lower-level objects for their methods. This module can use the introspection facility of Class::MOP and print a formated table by setting the object_methods option to "1" (as with lol this temporarily breaks the tree structure):
|__BLESSED OBJECT BELONGING TO CLASS: Statistics::PCA (3) ---RECURSING-INTO-OBJECT--- | --- .-----------------------------------------------------. | Methods | +-----------------------------------------------------+ | Statistics::PCA::_deep_copy_references | | Statistics::PCA::print_eigenvectors | | Statistics::PCA::_calculate_eigens_cephes | | ... | '-----------------------------------------------------' --- | |__HASH REFERENCE (3) | etc.
All options are passed by hash reference:
draw($data, {max_depth => 3, unwrap_objects => 1, object_methods => 1} );
Name: array_limit Description: Used in conjunction with long_array option. Specifies the cutoff point for printing an entire array of SCALARS and just first few example elements. Usage: draw($data, {array_limit => 6}); Values: 3-10. Default: 5.
Name: hash_key Description: Allows printing of just those branches pointed to by a particular HASH key of interest within a structure. Usage: draw($data, {hash_key => q{a_key_name}); Values: String. Default: undef.
Name: lol Description: This option suppresses long-outputs given from Lists-of-Lists - see OVERVIEW. Usage: draw($data, {lol => 2}); Values: 0, 1, 2. Default: 0.
Name: long_array Description: This option suppresses long-output from long arrays of SCALARS - see OVERVIEW. Usage: draw($data, {long_array => 2}); Values: 0, 1. Default: 0.
Name: max_depth Description: Specifies the maximum indentation/nesting depth to proceed to - see OVERVIEW. Usage: draw($data, {max_depth => 6}); Values: 0-10. Default: 10.
Name: max_methods Description: Used in conjunction with the object_methods option. Specifies the maximum number of object methods to print. Usage: draw($data, {max_methods => 6}); Values: 1-100. Default: 50.
Name: min_depth Description: Specifies the minimum indentation depth to start printing at - see OVERVIEW. Usage: draw($data, {array_limit => 6}); Values: 0-9. Default: 0.
Name: notation Description: This option enables or disables the automatic arrow "->" notation printing specifying how to dereference a particular entity within the structure. Usage: draw($data, {notation => 0}); Values: 0, 1. Default: 1.
Name: object_methods Description: Turns on object method introspection using the Class::MOP module. Usage: draw($data, {object_methods => 1}); Values: 0, 1. Default: 0.
Name: spaces Description: This option enables and disables the printing of extra "branch" lines in the Tree structure for easier visualisation. Usage: draw($data, {spaces => 0}); Values: 0, 1. Default: 1.
Name: scalar_val Description: Allows printing of just those SCALAR values possessing specific string values. Usage: draw($data, {scalar_val => q{a_value_of_interest}); Values: String. Default: undef.
Name: unwrap_objects Description: This option causes the program to recurse into objects that fall within a data structure. Usage: draw($data, {unwrap_objects => 1}); Values: 0, 1. Default: 1.
Name: borders - This option is not yet fully implemented. Description: Usage: Disabled atm as not yet fully implemented. Values: "0", "1". Default: "0" (off).
Scalar::Util => "1.22", Class::MOP => "0.95", Text::SimpleTable => "2.0", Carp => "1.08", 'MRO::Compat' => '0', # on Solaris
Daniel S. T. Hughes <dsth@cantab.net>
<dsth@cantab.net>
I just had a late night attempt at bug fixing. Please let me know if I broke it. Hash keys pointing to GLOBS, Code-Refs and Uninitialised ARRAYS, HASH refs now print (was a silly oversight). Thanks to the Perl Testers who let me know about an installation problem with Solaris. It should now work fine on Solaris.
Data::Dumper, Data::TreeDumper.
Copyright (c) 2009, Daniel S. T. Hughes <dsth@cantab.net>. All rights reserved.
This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See perlartistic.
because this software is licensed free of charge, there is no warranty for the software, to the extent permitted by applicable law. Except when otherwise stated in writing the copyright holders and/or other parties provide the software "as is" without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. The entire risk as to the quality and performance of the software is with you. Should the software prove defective, you assume the cost of all necessary servicing, repair, or correction.
In no event unless required by applicable law or agreed to in writing will any copyright holder, or any other party who may modify and/or redistribute the software as permitted by the above licence, be liable to you for damages, including any general, special, incidental, or consequential damages arising out of the use or inability to use the software (including but not limited to loss of data or data being rendered inaccurate or losses sustained by you or third parties or a failure of the software to operate with any other software), even if such holder or other party has been advised of the possibility of such damages.
1 POD Error
The following errors were encountered while parsing the POD:
Non-ASCII character seen before =encoding in 'it´s'. Assuming UTF-8
To install Data::TreeDraw, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Data::TreeDraw
CPAN shell
perl -MCPAN -e shell install Data::TreeDraw
For more information on module installation, please visit the detailed CPAN module installation guide.