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