Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks,

The JSON module has a few options for pretty printing, like adding spaces and indenting (see JSON on CPAN). However, the output can be a bit too pretty (i.e., unreadable because of too much whitespace). For example:

use JSON; %HoA = ('foo' => [1,2,3], 'bar' => ['a','b','c']); print "plain:\n".JSON->new->encode(\%HoA)."\n"; print "pretty:\n".JSON->new->pretty->encode(\%HoA);
prints
plain: {"foo":[1,2,3],"bar":["a","b","c"]} pretty: { "foo" : [ 1, 2, 3 ], "bar" : [ "a", "b", "c" ] }
Instead, I would like to keep arrays on one line while retaining newlines and indents for key-value pairs, i.e.:
{ "foo" : [1,2,3], "bar" : ["a","b","c"] }
What is the best way to do this? I could try to custom pretty print the plain string in Perl, but that seems to defy the purpose. Are there any other Perl to JSON modules, or pattern-based string printers, is it easy after all to reimplement such a pretty printer, or is altering JSON.pm a good option?

Replies are listed 'Best First'.
Re: customized JSON pretty print
by hdb (Monsignor) on Feb 16, 2015 at 12:37 UTC

    First use pretty and then remove superfluous whitespace:

    use JSON; sub remove_newlines { my $str = shift; $str =~ s/^\s+//gsm; $str =~ s/\n//gsm; return $str; } %HoA = ('foo' => [1,2,3], 'bar' => ['a','b','c']); print "plain:\n".JSON->new->encode(\%HoA)."\n"; my $pretty = JSON->new->pretty->encode(\%HoA); $pretty =~ s/(\[.*?\])/remove_newlines($1)/egsm; print "pretty:\n$pretty";

    This will not work with nested brackets or brackets within strings, though.

      Thank you for the quick reply, this is indeed a good solution for my example and a nice use of multi-line regexes. However for more complex JSON (the type one would usually want pretty printing for) this will not work, as you have noted. Any suggestions for that case?

      A related issue in pretty printing (sorry if this goes against the customs of the Monastery) is the ordering of keys. The JSON module allows to use the order provided by the Perl hash (which typically changes between executions) or to sort the keys alphabetically. However, I would like to use a custom sort order to improve readability of the output. Any thoughts?

        A quick look into JSON::PP reveals that there is a function array_to_json and in there a line

        my ($pre, $post) = $indent ? $self->_up_indent() : ('', '');

        If you change that to

        my ($pre, $post) = ('', '');

        pretty stops prettifying arrays.

        use JSON::PP; my %HoA = ('foo' => [1,2,3], 'bar' => ['a','b','c']); print "plain:\n".JSON::PP->new->encode(\%HoA)."\n"; print "pretty:\n".JSON::PP->new->pretty->encode(\%HoA);

        Custom sort or ASCIIbetical sort? ->canonical(1) on the json object will sort the keys in a stable manner.

        --MidLifeXis

Re: customized JSON pretty print
by LanX (Saint) on Feb 16, 2015 at 15:19 UTC
    The point is that prettiness is not easily defined.

    As soon your array had too many, probably long entries, you'd complain again about a messed up long unreadable line.

    That's why Data::Dump has a heuristic to inline short arrays with short entries.

    IMHO you can only ask a feature request at the maintainer of JSON.pm.

    perl -MData::Dump -e'@a=("aaa","bbb","ccc"); @b=(@a)x4; dd \@a,\@b' ( ["aaa", "bbb", "ccc"], [ "aaa", "bbb", "ccc", "aaa", "bbb", "ccc", "aaa", "bbb", "ccc", "aaa", "bbb", "ccc", ], )

    Data::Dumper OTOH doesn't have that heuristic, only global switches like you are asking for.

    Do you see the problem?

    Cheers Rolf

    PS: Je suis Charlie!