格式化数组的JSON输出

3
我希望JSON:XS能把数组元素输出在同一行。例如:
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;

这将输出:

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

而我的期望输出是:

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

2

这种行为是在模块中硬编码的。如果您愿意在计算机上打补丁来修改模块代码,这可以很容易地完成(以下说明适用于Linux CPAN):

  1. 进入CPAN构建目录 /root/.cpan/build/JSON-XS-3.01-*(实际名称末尾有一些随机字符)
  2. 将以下补丁应用到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. 运行make,然后运行make install

  2. 检查您的脚本:

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

一些必要的免责声明:在本地更改模块,风险自负。当然,正确的方式是制作一个好的补丁来接受相应的配置选项,并将此补丁提交给模块的作者。但是如果您只需要它在您的计算机上运行,那么这将完美地工作。


谢谢!这看起来不错,但我找不到XS.xs文件。此外,其他人(在其他机器上)将使用此程序,因此我认为修改本地源不是一个选项...最好向模块的作者提交补丁。无论如何,还是谢谢! - Håkon Hægland
你是手动从CPAN安装的还是使用软件包管理器安装的?如果你是从软件包安装的,那么请卸载它(rpm -e perl-JSON-XS或类似命令,根据你的系统而定),然后使用CPAN进行安装:perl -MCPAN -e 'install JSON::XS'。你将会有一个build目录,其中包含模块源代码,包括XS.xs文件。 - afenster
我曾经使用 cpanm JSON::XS 安装它(几个月前)。我正在使用 Perl 版本 5.18.2,在 Ubuntu 14.04 上。 - Håkon Hægland
是的,现在它可以工作了.. :) 非常感谢!现在有一些小问题我想要解决。1)如果数组中有一个数组,我们应该插入换行和缩进。2)如果数组的文本表示超过了 Term::ReadKey::GetTerminalSize(),则应该插入一个新行,并在上一行的 [ 位置处开始缩进继续下一行的数组。 - Håkon Hægland
可能比仅仅删除换行符要困难一些 :) 这是一个开源项目,你可以放心地按照自己的需求进行操作。 - afenster
显示剩余2条评论

0

如果数组不能包含散列,则可以采用以下解决方法:

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 );
}

输出:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接