Sometimes you *don't* want circular checking

I use the nifty Data::Rmap to "flatten" DateTime objects into strings so they can be exported to JSON and handled outside Perl. But due to circular checking in Data::Rmap, this:

$ perl -MData::Rmap=:all -MData::Dump \
-e'$d = DateTime->now; $doc = [$d, $d];
rmap_ref { $_ = $_->ymd if UNIVERSAL::isa($_, "DateTime") } $doc;
dd $doc'

produces something like this:

["2010-10-01", ...unconverted DateTime object...]

For now I work around this by defeating Data::Rmap's circular checking, though I wonder if there's a better way.

$ perl -MData::Rmap=:all -MData::Dump \
-e'$d = DateTime->now; $doc = [$d, $d];
rmap_ref { $_[0]{seen} = {}; $_ = $_->ymd if UNIVERSAL::isa($_, "DateTime") } $doc;
dd $doc'

will correctly produce:

["2010-10-01", "2010-10-01"]


I've had to deal with this before (because I always use DateTime objects) and I think the best method is to use the JSON module to jsut do the conversion for you. Here is your example:

perl -MDateTime -MJSON=2 -e '$d=DateTime->now;$doc=[$d,$d];{local *DateTime::TO_JSON=sub{$_[0]->ymd};print JSON->new->convert_blessed->encode($doc);}'

And in a more readable multi-line:

use DateTime;
use JSON 2;

my $now = DateTime->now;

my $doc = [$now, $now];

my $encoder = JSON->new->convert_blessed;

    # Add TO_JSON to DateTime for encoding
    local *DateTime::TO_JSON = sub { $_[0]->ymd };

    print $encoder->encode($doc);

Leave a comment

About Steven Haryanto

user-pic A programmer (mostly Perl 5 nowadays).