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

I'm trying to craft a script that will "tidy" up JSON, but am running into issues. The following script I've half-hazardly banged out inserts indents and newlines along any braces, but it doesnt take into account braces within quoted data, or indent regular name/value pairs, and I'm not quite sure of how to go about doing that...
#!/usr/bin/perl use strict; use warnings; # if first argument isnt a valid input file, die die("USAGE: $0 [input file]\n") if (! -f $ARGV[0]); # initialize some vars my $indent = my $sbrace = my $cbrace = 0; # slurp input file contents my $contents; { local $/ = undef; open(my $IN, '<', $ARGV[0]) or die("couldn't open file: $!"); $contents = <$IN>; close($IN); } # regex match every brace (with an optional trailing comma), evaluatin +g replace as sub $contents =~ s/([\{\}[\],],?)/indent_brace($1)/egmsx; $contents =~ s/\n+/\n/g; # print out entire file to original filename plus '.out' open(my $OUT, '>', $ARGV[0].'.out') or die("couldn't open file: $!"); print {$OUT} $contents; close($OUT); # where the REAL work is done sub indent_brace { # split into array of chars my @match = split //, shift; # define it in local scope my $result; # decrement indent and appropriate brace count if($match[0] eq ']' || $match[0] eq '}') { $indent--; $sbrace-- if $match[0] eq ']'; $cbrace-- if $match[0] eq '}'; } # inject newlines and indenting tabs if($match[0] eq ',') { $result = join '', @match, "\n"; } else { $result = join '', "\n", "\t"x$indent, @match, "\n"; } # increment indent and appropriate brace count if($match[0] eq '[' || $match[0] eq '{') { $indent++; $sbrace++ if $match[0] eq '['; $cbrace++ if $match[0] eq '{'; } return $result; }
Following is example JSON data, note the difficulties in that there may be braces and brackets within the quoted strings, as well as backslash-escaped quotes, which requires a much more complex regular expression (if that is even the way to go).
# untidy {"DDATE_ALPHA":"08\/17\/2007","TTIME_ALPHA":"18:23:09.592","IID_ALPHA" +:"[3858f62230ac3c915f300c664312c63f]","VDATA_ALPHA":[5,226426,565,224 +5,76437,754,3526,87245,6565,757,775,7563,3226,564572,45526,673,5278,1 +1584,556],"AAUTH_ALPHA":false,"RREF_ALPHA":null,"NNAME_ALPHA":"Versio +n \"0\", \"5\", \"19\""} # tidied up by hand (what I want the script output to look like) { "DDATE_ALPHA":"08\/17\/2007", "TTIME_ALPHA":"18:23:09.592", "IID_ALPHA":"[3858f62230ac3c915f300c664312c63f]", "VDATA_ALPHA":[ 5, 226426, 565, 2245, 76437, 754, 3526, 87245, 6565, 757, 775, 7563, 3226, 564572, 45526, 673, 5278, 11584, 556 ], "AAUTH_ALPHA":false, "RREF_ALPHA":null, "NNAME_ALPHA":"Version \"0\", \"5\", \"19\"" }
Any pointers? Am I just looking at this from the wrong angle? Thanks for any help at all.

Replies are listed 'Best First'.
Re: tidy up json via perl
by Trizor (Pilgrim) on Jul 18, 2007 at 16:40 UTC

    You may want to simply read and then write out the JSON using JSON or JSON::Syck to handle the parsing and re formatting for you. Unless you have malformed JSON one of those modules will be able to help you.

    If you can write your tidy system in a general enough way and say call it "jsontidy" you might consider sending it to the module author to include with the next release.