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