3

I would like JSON:XS to output array elements on a single line. For example:

use warnings;
use strict;
use JSON::XS;

my $h={a=>[1,2,3,4],b=>3};
my $coder = JSON::XS->new->pretty;
my $txt   = $coder->encode($h);
print $txt;

This gives output:

{
   "b" : 3,
   "a" : [
      1,
      2,
      3,
      4
   ]
}

whereas my desired output is:

{
   "b" : 3,
   "a" : [ 1, 2, 3, 4 ]
}

2 Answers 2

2

This behavior is hardcoded in the module. If you are ok with patching the module code on your computer it can be easily done (the following instructions are for Linux CPAN):

  1. Go to CPAN build directory /root/.cpan/build/JSON-XS-3.01-* (the actual name has some random characters at the end)
  2. Apply the following patch to XS.xs:
--- XS.xs.orig  2014-11-08 14:22:37.682348401 +0300
+++ XS.xs       2014-11-08 14:30:01.447643990 +0300
@@ -486,6 +486,15 @@
     encode_space (enc);
 }

+INLINE void
+encode_comma_singleline (enc_t *enc)
+{
+  encode_ch (enc, ',');
+
+  if (enc->json.flags & F_SPACE_AFTER)
+    encode_space (enc);
+}
+
 static void encode_sv (enc_t *enc, SV *sv);

 static void
@@ -500,24 +509,18 @@

   if (len >= 0)
     {
-      encode_nl (enc); ++enc->indent;
-
       for (i = 0; i <= len; ++i)
         {
           SV **svp = av_fetch (av, i, 0);

-          encode_indent (enc);
-
           if (svp)
             encode_sv (enc, *svp);
           else
             encode_str (enc, "null", 4, 0);

           if (i < len)
-            encode_comma (enc);
+            encode_comma_singleline (enc);
         }
-
-      encode_nl (enc); --enc->indent; encode_indent (enc);
     }

   encode_ch (enc, ']');
  1. Run make, then make install.

  2. Check your script:

$ perl json.pl
{
   "a" : [1, 2, 3, 4],
   "b" : 3
}

Some required disclaimer: accept changing the module locally at your own risk, the correct way of course is to make a good patch which would accept the corresponding configuration option, and submit this patch to the author of the module. But if you need it to run on your computer only, that will perfectly work.

Sign up to request clarification or add additional context in comments.

7 Comments

Thanks! This looks good, but I cannot find XS.xs file.. Also other people (on other machines) will use this program, so I think it is not an option to modify the source locally.. it would be better to submit a patch to the author of the module I think. Thanks anyway!
Did you install the package manually from CPAN or using the package manager? If you installed from the package, remove it (rpm -e perl-JSON-XS or something like that, depending on your system), and install using CPAN: perl -MCPAN -e 'install JSON::XS'. You will have a build directory with the module sources, including XS.xs.
Or maybe better to rebuild an RPM with this patch. Depends on which approach you prefer.
I used cpanm JSON::XS to install it (many months ago). I am on Ubuntu 14.04, using Perl version 5.18.2..
cpanm cleans up its working directory. Use cpanm --reinstall JSON::XS and then find the location of the build directory: find ~/.cpanm -name XS.xs. Then cd there — it's something like .cpanm/1415449323.2205/JSON-XS-3.01, of course with some different timestamp.
|
0

If arrays cannot contain hashes, the following could be workaround:

use warnings;
use strict;
use JSON::XS;
use Text::Balanced qw(extract_bracketed extract_delimited);
use Text::CSV;
my $csv = Text::CSV->new( { sep_char => ',', allow_whitespace => 1 } );

my $h = { a => "[", g => [ "[", 2, "bb]]", 4 ], b => 3, c => [ 1, 2, 3, 4 ] };
my $coder = JSON::XS->new->pretty;
my $txt   = $coder->encode($h);
my $str = "";
while (1) {
    my $ind1 = index( $txt, '"' );
    my $ind2 = index( $txt, '[' );
    if ( $ind1 >= 0 && $ind2 >= 0 ) {
        if ( $ind1 < $ind2 ) {
            skipQuoted( \$txt, \$str );
            next;
        }
    }
    elsif ( $ind2 < 0 ) {
        $str .= $txt;
        last;
    }
    my ( $etxt, $end, $beg ) = extract_bracketed( $txt, '["]', '[^[]*' );
    die "Unexpected!" if !defined $etxt;
    $str .= $beg;
    $etxt = substr( $etxt, 1, length($etxt) - 2 )
      ;    #strip leading and trailing brackets
    $etxt =~ s{\n}{}g;
    my @elem;
    if ( $csv->parse($etxt) ) {
        @elem = $csv->fields();
    }
    else {
        die "Unexpected!";
    }
    $str .= '[ ' . processFields( \@elem ) . ' ]';
    $txt = $end;
}

print $str;

sub skipQuoted {
    my ( $txt, $str ) = @_;

    my ( $s1, $s2, $s3 ) = extract_delimited( $$txt, '"', '[^"]*' );
    die "Unexpected!" if !defined $s1;
    $$str .= $s3 . $s1;
    $$txt = $s2;
}

sub processFields {
    my ($a) = @_;

    for (@$a) {
        if ( $_ !~ /^-?(0|([1-9][0-9]*))(\.[0-9]+)?([eE][-+]?[0-9]+)?$/ ) {
            $_ = '"' . $_ . '"';
        }
    }
    return join( ", ", @$a );
}

Output:

{
   "a" : "[",
   "g" : [ "[", 2, "bb]]", 4 ],
   "b" : 3,
   "c" : [ 1, 2, 3, 4 ]
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.