]> git.evergreen-ils.org Git - Evergreen.git/blob - OpenSRF/src/perlmods/JSON.pm
not escaping "/" characters
[Evergreen.git] / OpenSRF / src / perlmods / JSON.pm
1 package JSON::number;
2 sub new {
3         my $class = shift;
4         my $x = shift || $class;
5         return bless \$x => __PACKAGE__;
6 }
7 use overload ( '""' => \&toString );
8 use overload ( '0+' => sub { $_[0]->toString } );
9 use overload ( '+' => sub { int($_[0]) + int($_[1]) } );
10 use overload ( '-' => sub { int($_[0]) - int($_[1]) } );
11 use overload ( '*' => sub { int($_[0]) * int($_[1]) } );
12 use overload ( '/' => sub { int($_[0]) / int($_[1]) } );
13 use overload ( '%' => sub { int($_[0]) % int($_[1]) } );
14 use overload ( '**' => sub { int($_[0]) ** int($_[1]) } );
15 use overload ( 'neg' => sub { -int($_[0]) } );
16 use overload ( '==' => sub { int($_[0]->toString) == int($_[1])} );
17
18 sub toString { defined($_[1]) ? ${$_[1]} : ${$_[0]} }
19
20 package JSON::bool::true;
21 sub new { return bless {} => __PACKAGE__ }
22 use overload ( '""' => \&toString );
23 use overload ( 'bool' => sub { 1 } );
24 use overload ( '0+' => sub { 1 } );
25
26 sub toString { 'true' }
27
28 package JSON::bool::false;
29 sub new { return bless {} => __PACKAGE__ }
30 use overload ( '""' => \&toString );
31 use overload ( 'bool' => sub { 0 } );
32 use overload ( '0+' => sub { 0 } );
33
34 sub toString { 'false' }
35
36 package JSON;
37 use vars qw/%_class_map/;
38
39 sub register_class_hint {
40         my $class = shift;
41         my %args = @_;
42
43         $_class_map{hints}{$args{hint}} = \%args;
44         $_class_map{classes}{$args{name}} = \%args;
45 }
46
47 sub _JSON_regex {
48         my $string = shift;
49
50         $string =~ s/^\s* ( 
51                            {                            | # start object
52                            \[                           | # start array
53                            -?\d+\.?\d*                  | # number literal
54                            "(?:(?:\\[\"])|[^\"])*"      | # string literal
55                            (?:\/\*.+?\*\/)              | # C comment
56                            true                         | # bool true
57                            false                        | # bool false
58                            null                         | # undef()
59                            :                            | # object key-value sep
60                            ,                            | # list sep
61                            \]                           | # array end
62                            }                              # object end
63                         )
64                  \s*//sox;
65         return ($string,$1);
66 }
67
68 sub _json_hint_to_class {
69         my $type = shift;
70         my $hint = shift;
71
72         return $_class_map{hints}{$hint}{name} if (exists $_class_map{hints}{$hint});
73         
74         $type = 'hash' if ($type eq '}');
75         $type = 'array' if ($type eq ']');
76
77         JSON->register_class_hint(name => $hint, hint => $hint, type => $type);
78
79         return $hint;
80 }
81
82 sub JSON2perl {
83         my $class = shift;
84         local $_ = shift;
85
86         # Convert JSON Unicode...
87         s/\\u(\d{4})/chr(hex($1))/esog;
88
89         # handle class blessings
90         s/\/\*--\s*S\w*?\s+\S+\s*--\*\// bless(/sog;
91         s/(\]|\}|")\s*\/\*--\s*E\w*?\s+(\S+)\s*--\*\//$1 => _json_hint_to_class("$1", "$2")) /sog;
92
93         my $re = qr/((?<!\\)"(?>(?<=\\)"|[^"])*(?<!\\)")/;
94         # Grab strings...
95         my @strings = /$re/sog;
96
97         # Replace with code...
98         #s/"(?:(?:\\[\"])|[^\"])*"/ do{ \$t = '"'.shift(\@strings).'"'; eval \$t;} /sog;
99         s/$re/ eval shift(\@strings) /sog;
100
101         # Perlify hash notation
102         s/:/ => /sog;
103
104         # Do numbers...
105         s/\b(-?\d+\.?\d*)\b/ JSON::number::new($1) /sog;
106
107         # Change javascript stuff to perl...
108         s/null/ undef /sog;
109         s/true/ bless( {}, "JSON::bool::true") /sog;
110         s/false/ bless( {}, "JSON::bool::false") /sog;
111
112         my $ret;
113         return eval '$ret = '.$_;
114 }
115
116 sub old_JSON2perl {
117         my ($class, $json) = @_;
118
119         if (!defined($json)) {
120                 return undef;
121         }
122
123         $json =~ s/(?<!\\)\$/\\\$/gmo; # fixup $ for later
124         $json =~ s/(?<!\\)\@/\\\@/gmo; # fixup @ for later
125
126         my @casts;
127         my $casting_depth = 0;
128         my $current_cast;
129         my $element;
130         my $output = '';
131         while (($json,$element) = _JSON_regex($json)) {
132
133                 last unless ($element);
134
135                 if ($element eq 'null') {
136                         $output .= ' undef() ';
137                         next;
138                 } elsif ($element =~ /^\/\*--\s*S\w*?\s+(\w+)\s*--\*\/$/) {
139                         my $hint = $1;
140                         if (exists $_class_map{hints}{$hint}) {
141                                 $casts[$casting_depth] = $hint;
142                                 $output .= ' bless(';
143                         }
144                         next;
145                 } elsif ($element =~ /^\/\*/) {
146                         next;
147                 } elsif ($element =~ /^\d/) {
148                         $output .= "do { JSON::number::new($element) }";
149                         next;
150                 } elsif ($element eq '{' or $element eq '[') {
151                         $casting_depth++;
152                 } elsif ($element eq '}' or $element eq ']') {
153                         $casting_depth--;
154                         my $hint = $casts[$casting_depth];
155                         $casts[$casting_depth] = undef;
156                         if (defined $hint and exists $_class_map{hints}{$hint}) {
157                                 $output .= $element . ',"'. $_class_map{hints}{$hint}{name} . '")';
158                                 next;
159                         }
160                 } elsif ($element eq ':') {
161                         $output .= ' => ';
162                         next;
163                 } elsif ($element eq 'true') {
164                         $output .= 'bless( {}, "JSON::bool::true")';
165                         next;
166                 } elsif ($element eq 'false') {
167                         $output .= 'bless( {}, "JSON::bool::false")';
168                         next;
169                 }
170                 
171                 $output .= $element;
172         }
173
174         return eval $output;
175 }
176
177 sub perl2JSON {
178         my ($class, $perl) = @_;
179
180         my $output = '';
181         if (!defined($perl)) {
182                 $output = 'null';
183         } elsif (ref($perl) and ref($perl) =~ /^JSON/) {
184                 $output .= $perl;
185         } elsif ( ref($perl) && exists($_class_map{classes}{ref($perl)}) ) {
186                 $output .= '/*--S '.$_class_map{classes}{ref($perl)}{hint}.'--*/';
187                 if (lc($_class_map{classes}{ref($perl)}{type}) eq 'hash') {
188                         my %hash =  %$perl;
189                         $output .= perl2JSON(undef,\%hash);
190                 } elsif (lc($_class_map{classes}{ref($perl)}{type}) eq 'array') {
191                         my @array =  @$perl;
192                         $output .= perl2JSON(undef,\@array);
193                 }
194                 $output .= '/*--E '.$_class_map{classes}{ref($perl)}{hint}.'--*/';
195         } elsif (ref($perl) and ref($perl) =~ /HASH/) {
196                 $output .= '{';
197                 my $c = 0;
198                 for my $key (sort keys %$perl) {
199                         $output .= ',' if ($c); 
200                         
201                         $output .= perl2JSON(undef,$key).':'.perl2JSON(undef,$$perl{$key});
202                         $c++;
203                 }
204                 $output .= '}';
205         } elsif (ref($perl) and ref($perl) =~ /ARRAY/) {
206                 $output .= '[';
207                 my $c = 0;
208                 for my $part (@$perl) {
209                         $output .= ',' if ($c); 
210                         
211                         $output .= perl2JSON(undef,$part);
212                         $c++;
213                 }
214                 $output .= ']';
215         } elsif (ref($perl) and ("$perl" =~ /^([^=]+)=(\w+)/o)) {
216                 my $type = $2;
217                 my $name = $1;
218                 JSON->register_class_hint(name => $name, hint => $name, type => lc($type));
219                 $output .= perl2JSON(undef,$perl);
220         } else {
221                 $perl =~ s{\\}{\\\\}sgo;
222                 $perl =~ s/"/\\"/sgo;
223                 $perl =~ s/\t/\\t/sgo;
224                 $perl =~ s/\f/\\f/sgo;
225                 $perl =~ s/\r/\\r/sgo;
226                 $perl =~ s/\n/\\n/sgo;
227                 $perl =~ s/(\pM)/sprintf('\u%0.4x',ord($1))/sgoe;
228                 $output = '"'.$perl.'"';
229         }
230
231         return $output;
232 }
233
234 my $depth = 0;
235 sub perl2prettyJSON {
236         my ($class, $perl, $nospace) = @_;
237         $perl ||= $class;
238
239         my $output = '';
240         if (!defined($perl)) {
241                 $output = "   "x$depth unless($nospace);
242                 $output .= 'null';
243         } elsif (ref($perl) and ref($perl) =~ /^JSON/) {
244                 $output = "   "x$depth unless($nospace);
245                 $output .= $perl;
246         } elsif ( ref($perl) && exists($_class_map{classes}{ref($perl)}) ) {
247                 $depth++;
248                 $output .= "\n";
249                 $output .= "   "x$depth;
250                 $output .= '/*--S '.$_class_map{classes}{ref($perl)}{hint}."--*/ ";
251                 if (lc($_class_map{classes}{ref($perl)}{type}) eq 'hash') {
252                         my %hash =  %$perl;
253                         $output .= perl2prettyJSON(\%hash,undef,1);
254                 } elsif (lc($_class_map{classes}{ref($perl)}{type}) eq 'array') {
255                         my @array =  @$perl;
256                         $output .= perl2prettyJSON(\@array,undef,1);
257                 }
258                 $output .= ' /*--E '.$_class_map{classes}{ref($perl)}{hint}.'--*/';
259                 $depth--;
260         } elsif (ref($perl) and ref($perl) =~ /HASH/) {
261                 $output .= "   "x$depth unless ($nospace);
262                 $output .= "{\n";
263                 my $c = 0;
264                 $depth++;
265                 for my $key (sort keys %$perl) {
266                         $output .= ",\n" if ($c); 
267                         
268                         $output .= perl2prettyJSON($key)." : ".perl2prettyJSON($$perl{$key}, undef, 1);
269                         $c++;
270                 }
271                 $depth--;
272                 $output .= "\n";
273                 $output .= "   "x$depth;
274                 $output .= '}';
275         } elsif (ref($perl) and ref($perl) =~ /ARRAY/) {
276                 $output .= "   "x$depth unless ($nospace);
277                 $output .= "[\n";
278                 my $c = 0;
279                 $depth++;
280                 for my $part (@$perl) {
281                         $output .= ",\n" if ($c); 
282                         
283                         $output .= perl2prettyJSON($part);
284                         $c++;
285                 }
286                 $depth--;
287                 $output .= "\n";
288                 $output .= "   "x$depth;
289                 $output .= "]";
290         } elsif (ref($perl) and "$perl" =~ /^([^=]+)=(\w{4,5})\(0x/) {
291                 my $type = $2;
292                 my $name = $1;
293                 register_class_hint(undef, name => $name, hint => $name, type => lc($type));
294                 $output .= perl2prettyJSON(undef,$perl);
295         } else {
296                 $perl =~ s/\\/\\\\/sgo;
297                 $perl =~ s/"/\\"/sgo;
298                 $perl =~ s/\t/\\t/sgo;
299                 $perl =~ s/\f/\\f/sgo;
300                 $perl =~ s/\r/\\r/sgo;
301                 $perl =~ s/\n/\\n/sgo;
302                 $perl =~ s/(\pM)/sprintf('\u%0.4x',ord($1))/sgoe;
303                 $output .= "   "x$depth unless($nospace);
304                 $output .= '"'.$perl.'"';
305         }
306
307         return $output;
308 }
309
310 1;