ZetCode

Perl Data::Dumper and Data::Printer

last modified November 7, 2026

Perl programs build nested data structures out of hashes, arrays, and references. Printing such a structure with print only shows something like HASH(0x55d1c0a8e2a8). The core module Data::Dumper turns any structure into readable Perl source code, which makes it the standard tool for a quick look at data while debugging. The CPAN module Data::Printer produces a more compact view meant for humans, with extra details about the values.

Data::Dumper ships with Perl; Data::Printer is installed with cpanm Data::Printer. The examples were run with Perl 5.44, Data::Dumper 2.192, and Data::Printer 1.002001 on Linux.

Dumping a structure

The Dumper function takes a list of values and returns a string. Structures are passed as references.

basic.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;
use Data::Dumper;

my %user = (
    name   => 'Tomas',
    age    => 34,
    langs  => ['Perl', 'Go', 'SQL'],
    address => {
        city => 'Kosice',
        zip  => '04001',
    },
);

my @scores = (88, 92.5, 'n/a');

print Dumper(\%user);
print Dumper(\@scores, 'plain string');

Each value is printed as an assignment to a variable named $VAR1, $VAR2, and so on, so the output is valid Perl code. Nested hashes and arrays are shown with braces and brackets. Numbers that are stored as integers appear without quotes, while other values, including the floating-point 92.5, are quoted. The keys of %user come out in Perl's random hash order, which differs from run to run; the next section fixes that.

$ perl basic.pl
$VAR1 = {
          'age' => 34,
          'address' => {
                         'zip' => '04001',
                         'city' => 'Kosice'
                       },
          'name' => 'Tomas',
          'langs' => [
                       'Perl',
                       'Go',
                       'SQL'
                     ]
        };
$VAR1 = [
          88,
          '92.5',
          'n/a'
        ];
$VAR2 = 'plain string';

Indent, Sortkeys, and Terse

The output is controlled by package variables of the form $Data::Dumper::Name. Three of them are set in almost every program.

settings.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;
use Data::Dumper;

my $data = { b => [1, 2], a => { x => 1 }, c => 'text' };

$Data::Dumper::Sortkeys = 1;

for my $indent (0, 1, 2) {
    local $Data::Dumper::Indent = $indent;
    say "Indent = $indent:";
    print Dumper($data);
    print "\n" if $indent == 0;
}

{
    local $Data::Dumper::Terse = 1;
    local $Data::Dumper::Indent = 1;
    say 'Terse = 1:';
    print Dumper($data);
}

Sortkeys set to a true value sorts hash keys alphabetically, which makes the output stable and comparable between runs. Indent chooses the layout: 0 puts everything on one line, 1 indents each level by a fixed two spaces, and the default 2 aligns nested structures under their opening bracket, which quickly wastes width. Terse drops the $VAR1 = prefix and the final semicolon and prints just the value. Using local limits a change to the enclosing block, so other code that uses the module is not affected.

$ perl settings.pl
Indent = 0:
$VAR1 = {'a' => {'x' => 1},'b' => [1,2],'c' => 'text'};
Indent = 1:
$VAR1 = {
  'a' => {
    'x' => 1
  },
  'b' => [
    1,
    2
  ],
  'c' => 'text'
};
Indent = 2:
$VAR1 = {
          'a' => {
                   'x' => 1
                 },
          'b' => [
                   1,
                   2
                 ],
          'c' => 'text'
        };
Terse = 1:
{
  'a' => {
    'x' => 1
  },
  'b' => [
    1,
    2
  ],
  'c' => 'text'
}

The object interface

Global variables affect every caller in the program. The object interface keeps the settings in one dumper object and also lets us name the variables.

oo.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;
use Data::Dumper;

my $config = {
    port    => 8080,
    hosts   => ['alpha', 'beta'],
    debug   => 0,
    message => "Hello\tworld\n",
};

my @langs = qw(Perl Raku);

my $d = Data::Dumper->new([$config, \@langs], [qw(config *langs)]);
$d->Indent(1)->Sortkeys(1)->Useqq(1);

print $d->Dump;

say Data::Dumper->new([$config])->Terse(1)->Indent(0)->Sortkeys(1)->Dump;

The constructor takes a reference to the list of values and a reference to the list of names. A name starting with * dumps an array or hash reference as a real array or hash, here @langs = (...). Each setting has a method of the same name that returns the object, so the calls chain. Useqq uses double-quoted strings with escapes, which makes the tab and the newline in message visible. The last line creates a second dumper without Useqq; its single-quoted output contains the raw tab and newline characters, which break the line in the middle of the string.

$ perl oo.pl
$config = {
  "debug" => 0,
  "hosts" => [
    "alpha",
    "beta"
  ],
  "message" => "Hello\tworld\n",
  "port" => 8080
};
@langs = (
  "Perl",
  "Raku"
);
{'debug' => 0,'hosts' => ['alpha','beta'],'message' => 'Hello	world
','port' => 8080}

Custom key order

Sortkeys also accepts a code reference. It receives each hash reference to be dumped and returns a reference to an array of the keys to show, in the order to show them.

sortkeys.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;
use Data::Dumper;

my %record = (id => 7, name => 'Lamp', price => 19.9, _internal => 'x', created => '2026-11-07');

local $Data::Dumper::Indent = 1;
local $Data::Dumper::Terse  = 1;

local $Data::Dumper::Sortkeys = sub {
    my $h = shift;
    my @keys = grep { !/^_/ } keys %$h;
    my %rank = (id => 0, name => 1);
    return [ sort { ($rank{$a} // 9) <=> ($rank{$b} // 9) or $a cmp $b } @keys ];
};

print Dumper(\%record);

The function hides keys that start with an underscore and puts id and name first, followed by the remaining keys in alphabetical order. Since only the returned keys are printed, the same mechanism filters out bulky or sensitive fields from debug output.

$ perl sortkeys.pl
{
  'id' => 7,
  'name' => 'Lamp',
  'created' => '2026-11-07',
  'price' => '19.9'
}

Limiting the depth

A deep structure can produce pages of output. Maxdepth stops descending after the given number of levels.

depth.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;
use Data::Dumper;

my $tree = {
    name => 'root',
    kids => [
        { name => 'a', kids => [ { name => 'a1', kids => [] } ] },
        { name => 'b', kids => [] },
    ],
};

local $Data::Dumper::Sortkeys = 1;
local $Data::Dumper::Terse    = 1;
local $Data::Dumper::Indent   = 1;
local $Data::Dumper::Maxdepth = 2;

print Dumper($tree);

With Maxdepth 2, the dumper shows the top-level hash and the kids array, but prints the hashes inside the array as their addresses. The addresses differ between runs. This gives a quick overview of the shape of large data, for example a decoded JSON response, before looking at the details.

$ perl depth.pl
{
  'kids' => [
    'HASH(0x58b64839c9b8)',
    'HASH(0x58b64839cbf8)'
  ],
  'name' => 'root'
}

Shared references, cycles, and objects

Data::Dumper notices when the same reference appears more than once, including references that point back to themselves, and shows blessed references and code references as well.

refs.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;
use Data::Dumper;

$Data::Dumper::Sortkeys = 1;
$Data::Dumper::Indent   = 1;

my $shared = { color => 'red' };
my $pair = { left => $shared, right => $shared };

print Dumper($pair);

my $node = { name => 'loop' };
$node->{self} = $node;

print Dumper($node);

my $obj = bless { id => 42 }, 'My::Item';
my $code = sub { return 1 };

print Dumper($obj, $code);

The second occurrence of a shared hash is printed as a path to the first one, $VAR1->{'left'}, so it is clear that both keys refer to the same hash and not to two equal copies. A cycle is shown in the same way, which keeps the dumper from recursing forever. An object is printed with a bless call naming its class. Code is not dumped; a code reference appears as sub { "DUMMY" } unless $Data::Dumper::Deparse is set, in which case the module decompiles the subroutine with B::Deparse.

$ perl refs.pl
$VAR1 = {
  'left' => {
    'color' => 'red'
  },
  'right' => $VAR1->{'left'}
};
$VAR1 = {
  'name' => 'loop',
  'self' => $VAR1
};
$VAR1 = bless( {
  'id' => 42
}, 'My::Item' );
$VAR2 = sub { "DUMMY" };

Reading the data back

Since the output is Perl code, evaluating it rebuilds the structure. This makes Data::Dumper a simple serialization format for Perl-only data.

roundtrip.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;
use Data::Dumper;

my $settings = {
    theme  => 'dark',
    recent => ['notes.txt', 'todo.md'],
    window => { width => 1024, height => 768 },
};

my $text = Data::Dumper->new([$settings])->Terse(1)->Indent(1)->Sortkeys(1)->Dump;

open my $out, '>', 'settings.txt' or die "settings.txt: $!";
print $out $text;
close $out;

open my $in, '<', 'settings.txt' or die "settings.txt: $!";
my $code = do { local $/; <$in> };
close $in;

my $loaded = eval $code;
die "cannot load settings: $@" if $@;

print $code;
say "theme: $loaded->{theme}";
say "width: $loaded->{window}{width}";
say "recent: @{ $loaded->{recent} }";

The structure is dumped in Terse mode, so the file contains just an expression, and a string eval returns its value. A structure with shared or circular references needs the Purity setting, which adds statements to restore the links. Evaluating a file runs arbitrary code, so this is acceptable only for files that the program wrote itself. For data exchanged with other programs or users, JSON is the safer and more portable choice.

$ perl roundtrip.pl
{
  'recent' => [
    'notes.txt',
    'todo.md'
  ],
  'theme' => 'dark',
  'window' => {
    'height' => 768,
    'width' => 1024
  }
}
theme: dark
width: 1024
recent: notes.txt todo.md

Data::Printer

Data::Printer is designed for reading rather than for evaluation. Its p function prints a variable to standard error, and np returns the text instead. The short alias DDP loads the module.

ddp.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;
use DDP colored => 0;

my %user = (
    name    => 'Tomas',
    age     => 34,
    langs   => ['Perl', 'Go', 'SQL'],
    address => { city => 'Kosice', zip => '04001' },
    active  => undef,
);

p %user;

my @matrix = ([1, 2], [3, 4]);
p @matrix;

my $text = np @matrix, multiline => 0;
say "as string: $text";

Unlike Dumper, p takes the variable itself, not a reference. Keys are sorted, array elements are numbered, and strings are quoted while numbers are not. The value undef is shown explicitly, and the module points out that the string "04001" also has the numeric value 4001, a detail that matters for postal codes and identifiers. Options follow the variable; multiline => 0 produces a single line. Colours are on by default when writing to a terminal; the example turns them off with colored => 0 so the output can be shown here.

$ perl ddp.pl
{
    active    undef,
    address   {
        city   "Kosice",
        zip    "04001" (dualvar: 4001)
    },
    age       34,
    langs     [
        [0] "Perl",
        [1] "Go",
        [2] "SQL"
    ],
    name      "Tomas"
}
[
    [0] [
            [0] 1,
            [1] 2
        ],
    [1] [
            [0] 3,
            [1] 4
        ]
]
as string: [ [ 1, 2 ], [ 3, 4 ] ]

Objects with Data::Printer

For objects, Data::Printer shows more than the internal hash: it lists the class's methods and the inheritance tree.

ddp_obj.pl
#!/usr/bin/perl

package Counter;

sub new { my ($class, %args) = @_; return bless { count => $args{start} // 0 }, $class }
sub inc { $_[0]{count}++ }
sub value { $_[0]{count} }

package main;

use strict;
use warnings;
use v5.34.0;
use DDP colored => 0;

my $c = Counter->new(start => 5);
$c->inc;

p $c;

my $data = { regex => qr/^\d+$/i, code => \&Counter::inc, num => 3.14, str => '3.14' };
p $data;

The Counter object is printed with its public and private methods, where private means a name starting with an underscore, followed by its internal data. A regular expression is shown as its pattern with its modifiers, and a code reference as sub { ... }. Settings can be stored in a .dataprinter file in the home directory, so a project does not need to repeat them in every use line.

$ perl ddp_obj.pl
Counter  {
    public methods (3): inc, new, value
    private methods (0)
    internals: {
        count   6
    }
}
{
    code    sub { ... },
    num     3.14,
    regex   ^\d+$  (modifiers: ui),
    str     3.14
}

Best practices

In this article we have inspected data structures with Data::Dumper, configured its output with Indent, Sortkeys, Terse, Useqq, and Maxdepth, dumped shared references, cycles, and objects, read a dump back with eval, and explored data with Data::Printer.

Source

Data::Dumper ยท Data::Printer on MetaCPAN

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Perl tutorials.