1#!/opt/bin/perl
2
3=head1 NAME
4
5json_xs - JSON::XS commandline utility
6
7=head1 SYNOPSIS
8
9   json_xs [-v] [-f inputformat] [-t outputformat]
10
11=head1 DESCRIPTION
12
13F<json_xs> converts between some input and output formats (one of them is
14JSON).
15
16The default input format is C<json> and the default output format is
17C<json-pretty>.
18
19=head1 OPTIONS
20
21=over 4
22
23=item -v
24
25Be slightly more verbose.
26
27=item -f fromformat
28
29Read a file in the given format from STDIN.
30
31C<fromformat> can be one of:
32
33=over 4
34
35=item json - a json text encoded, either utf-8, utf16-be/le, utf32-be/le
36
37=item cbor - CBOR (RFC 7049, L<CBOR::XS>), a kind of binary JSON
38
39=item storable - a L<Storable> frozen value
40
41=item storable-file - a L<Storable> file (Storable has two incompatible formats)
42
43=item bencode - use L<Convert::Bencode>, if available (used by torrent files, among others)
44
45=item clzf - L<Compress::LZF> format (requires that module to be installed)
46
47=item eval - evaluate the given code as (non-utf-8) Perl, basically the reverse of "-t dump"
48
49=item yaml - L<YAML> (avoid at all costs, requires the YAML module :)
50
51=item string - do not attempt to decode the file data
52
53=item none - nothing is read, creates an C<undef> scalar - mainly useful with C<-e>
54
55=back
56
57=item -t toformat
58
59Write the file in the given format to STDOUT.
60
61C<toformat> can be one of:
62
63=over 4
64
65=item json, json-utf-8 - json, utf-8 encoded
66
67=item json-pretty - as above, but pretty-printed
68
69=item json-utf-16le, json-utf-16be - little endian/big endian utf-16
70
71=item json-utf-32le, json-utf-32be - little endian/big endian utf-32
72
73=item cbor - CBOR (RFC 7049, L<CBOR::XS>), a kind of binary JSON
74
75=item storable - a L<Storable> frozen value in network format
76
77=item storable-file - a L<Storable> file in network format (Storable has two incompatible formats)
78
79=item bencode - use L<Convert::Bencode>, if available (used by torrent files, among others)
80
81=item clzf - L<Compress::LZF> format
82
83=item yaml - L<YAML>
84
85=item dump - L<Data::Dump>
86
87=item dumper - L<Data::Dumper>
88
89=item string - writes the data out as if it were a string
90
91=item none - nothing gets written, mainly useful together with C<-e>
92
93Note that Data::Dumper doesn't handle self-referential data structures
94correctly - use "dump" instead.
95
96=back
97
98=item -e code
99
100Evaluate perl code after reading the data and before writing it out again
101- can be used to filter, create or extract data. The data that has been
102written is in C<$_>, and whatever is in there is written out afterwards.
103
104=back
105
106=head1 EXAMPLES
107
108   json_xs -t none <isitreally.json
109
110"JSON Lint" - tries to parse the file F<isitreally.json> as JSON - if it
111is valid JSON, the command outputs nothing, otherwise it will print an
112error message and exit with non-zero exit status.
113
114   <src.json json_xs >pretty.json
115
116Prettify the JSON file F<src.json> to F<dst.json>.
117
118   json_xs -f storable-file <file
119
120Read the serialised Storable file F<file> and print a human-readable JSON
121version of it to STDOUT.
122
123   json_xs -f storable-file -t yaml <file
124
125Same as above, but write YAML instead (not using JSON at all :)
126
127   json_xs -f none -e '$_ = [1, 2, 3]'
128
129Dump the perl array as UTF-8 encoded JSON text.
130
131   <torrentfile json_xs -f bencode -e '$_ = join "\n", map @$_, @{$_->{"announce-list"}}' -t string
132
133Print the tracker list inside a torrent file.
134
135   lwp-request http://cpantesters.perl.org/show/JSON-XS.json | json_xs
136
137Fetch the cpan-testers result summary C<JSON::XS> and pretty-print it.
138
139=head1 AUTHOR
140
141Copyright (C) 2008 Marc Lehmann <json@schmorp.de>
142
143=cut
144
145use strict;
146
147use Getopt::Long;
148use Storable ();
149use Encode;
150
151use JSON::XS;
152
153my $opt_verbose;
154my $opt_from = "json";
155my $opt_to   = "json-pretty";
156my $opt_eval;
157
158Getopt::Long::Configure ("bundling", "no_ignore_case", "require_order");
159
160GetOptions(
161   "v"   => \$opt_verbose,
162   "f=s" => \$opt_from,
163   "t=s" => \$opt_to,
164   "e=s" => \$opt_eval,
165) or die "Usage: $0 [-v] -f fromformat [-e code] [-t toformat]\n";
166
167my %F = (
168   "none"          => sub { undef },
169   "string"        => sub { $_ },
170   "json"          => sub {
171      my $enc =
172         /^\x00\x00\x00/s  ? "utf-32be"
173       : /^\x00.\x00/s     ? "utf-16be"
174       : /^.\x00\x00\x00/s ? "utf-32le"
175       : /^.\x00.\x00/s    ? "utf-16le"
176       :                     "utf-8";
177      warn "input text encoding is $enc\n" if $opt_verbose;
178      JSON::XS->new->decode (decode $enc, $_)
179   },
180   "cbor"          => sub { require CBOR::XS; CBOR::XS::decode_cbor ($_) },
181   "storable"      => sub { Storable::thaw $_ },
182   "storable-file" => sub { open my $fh, "<", \$_; Storable::fd_retrieve $fh },
183   "bencode"       => sub { require Convert::Bencode; Convert::Bencode::bdecode ($_) },
184   "clzf"          => sub { require Compress::LZF; Compress::LZF::sthaw ($_) },
185   "yaml"          => sub { require YAML; YAML::Load ($_) },
186   "eval"          => sub { my $v = eval "no strict; no warnings; no utf8;\n#line 1 \"input\"\n$_"; die "$@" if $@; $v },
187);
188
189my %T = (
190   "none"          => sub { "" },
191   "string"        => sub { $_ },
192   "json"          => sub { encode_json $_ },
193   "json-utf-8"    => sub { encode_json $_ },
194   "json-pretty"   => sub { JSON::XS->new->utf8->pretty->encode ($_) },
195   "json-utf-16le" => sub { encode "utf-16le", JSON::XS->new->encode ($_) },
196   "json-utf-16be" => sub { encode "utf-16be", JSON::XS->new->encode ($_) },
197   "json-utf-32le" => sub { encode "utf-32le", JSON::XS->new->encode ($_) },
198   "json-utf-32be" => sub { encode "utf-32be", JSON::XS->new->encode ($_) },
199   "cbor"          => sub { require CBOR::XS; CBOR::XS::encode_cbor ($_) },
200   "storable"      => sub { Storable::nfreeze $_ },
201   "storable-file" => sub { open my $fh, ">", \my $buf; Storable::nstore_fd $_, $fh; $buf },
202   "bencode"       => sub { require Convert::Bencode; Convert::Bencode::bencode ($_) },
203   "clzf"          => sub { require Compress::LZF; Compress::LZF::sfreeze_cr ($_) },
204   "yaml"          => sub { require YAML; YAML::Dump ($_) },
205   "dumper"        => sub {
206      require Data::Dumper;
207      #local $Data::Dumper::Purity    = 1; # hopeless case
208      local $Data::Dumper::Terse     = 1;
209      local $Data::Dumper::Indent    = 1;
210      local $Data::Dumper::Useqq     = 1;
211      local $Data::Dumper::Quotekeys = 0;
212      local $Data::Dumper::Sortkeys  = 1;
213      Data::Dumper::Dumper($_)
214   },
215   "dump"          => sub {
216      require Data::Dump;
217      local $Data::Dump::TRY_BASE64 = 0;
218      Data::Dump::dump ($_) . "\n"
219   },
220);
221
222$F{$opt_from}
223   or die "$opt_from: not a valid fromformat\n";
224
225$T{$opt_to}
226   or die "$opt_from: not a valid toformat\n";
227
228if ($opt_from ne "none") {
229   local $/;
230   binmode STDIN; # stupid perl sometimes thinks its funny
231   $_ = <STDIN>;
232}
233
234$_ = $F{$opt_from}->();
235
236eval $opt_eval;
237die $@ if $@;
238
239$_ = $T{$opt_to}->();
240
241binmode STDOUT;
242syswrite STDOUT, $_;
243
244
245
246