File Coverage

blib/lib/Template/Plugin/deJSON.pm
Criterion Covered Total %
statement 61 63 96.8
branch 13 16 81.2
condition n/a
subroutine 7 8 87.5
pod 1 2 50.0
total 82 89 92.1


line stmt bran cond sub pod time code
1             package Template::Plugin::deJSON;
2              
3             =head1 NAME
4              
5             Template::Plugin::DeJSON - de-JSONify a JSON string
6              
7             =head1 SYNOPSIS
8              
9             [%
10             USE deJSON;
11             hash = deJSON.deJSON(json_string);
12             FOREACH field=hash;
13             field; field.value;
14             END;
15             %]
16              
17             =head1 DESCRIPTION
18              
19             Well, I needed this. I had JSON string things flying around between servers,
20             and passed into templates. (If you must know, objects were stringified using
21             JSON, and bit-shifted around the world.) It seemed to me I needed a plugin to
22             take those strings and turn them into something a bit more useful.
23              
24             So it takes a JSON string, and gives you back a hash. Or me. It gives it back
25             to me. YMMV.
26              
27             It also copes with JSON strings within JSON strings, returning a nice data
28             structure where the values themselves might be hashes. This is good. It means
29             keys don't get overwritten. Again, it works on my machine for what I want it
30             to do. YMM(again)V.
31              
32             =cut
33              
34 1     1   773 use strict;
  1         2  
  1         46  
35 1     1   6 use warnings;
  1         2  
  1         39  
36              
37 1     1   16 use base 'Template::Plugin';
  1         3  
  1         831  
38              
39             our $VERSION = 0.03;
40              
41             sub new {
42 0     0 1 0 my ($class, $context) = @_;
43 0         0 bless {
44             _CONTEXT => $context,
45             }, $class;
46             }
47              
48             sub _balance {
49 4     4   10 my ($self, $string) = @_;
50 4         6 my $index = 0; my (@opens, @closes);
  4         4  
51 4         13 while ($index >= 0) {
52 16         28 my $pos = index $string, '{', $index;
53 16 100       36 last if $pos < 0;
54 12 50       33 push @opens, $pos unless (substr($string, $pos - 1, 1) eq '\\');
55 12         27 $index = $pos + 1;
56             }
57 4         6 $index = 0;
58 4         9 while ($index >= 0) {
59 16         22 my $pos = index $string, '}', $index;
60 16 100       32 last if $pos < 0;
61 12 50       28 push @closes, $pos unless (substr($string, $pos - 1, 1) eq '\\');
62 12         27 $index = $pos + 1;
63             }
64 4 50       14 die "Unbalanced" unless scalar @opens == scalar @closes;
65 4         17 my @stack = ([ shift(@opens), pop(@closes) ]);
66 4         11 for my $start (reverse @opens) {
67 8         9 my $brack = $closes[-1];
68 8         16 for my $end (@closes) {
69 17 100       45 $brack = $end if $end > $start;
70             }
71 8         14 @closes = grep { $_ ne $brack } @closes;
  17         45  
72 8         24 push @stack, [ $start, $brack ];
73             }
74 4         16 return @stack;
75             }
76              
77             sub _inflate {
78 4     4   8 my ($self, $string) = @_;
79 4         9 my @coords = $self->_balance($string);
80 4         6 my $outer = shift @coords;
81 4         8 my %all;
82 4         8 my ($SPACER1, $SPACER2, $offset) = ('#!#_#mwk', 'mwk!__!__!', 0);
83 4         8 for my $pos (@coords) {
84 8         23 my $substr = substr $string, $pos->[0] + $offset, $pos->[1] - $pos->[0];
85 8         220 $string =~ m/"(\w+)":\Q$substr/;
86 8         27 my $name = $1;
87 8         119 (my $info = $substr) =~ s/({|}|")//g;
88 8         25 $all{$name} = { map { split /:/, $_ } split /,/, $info };
  19         73  
89 8         195 (my $replace = $substr) =~ s/./=/g;
90 8         190 $string =~ s/$substr/$replace/;
91             }
92 4         82 $string =~ s/({|}|")//g;
93 4         12 return { map { split /:/, $_ } split /,/, $string }, { %all };
  16         59  
94             }
95              
96             sub _structure {
97 4     4   7 my ($self, $string) = @_;
98 4         13 my ($master, $replaces) = $self->_inflate($string);
99 4         17 for my $key (keys %$replaces) {
100 8         8 for my $inner (keys %{ $replaces->{$key} }) {
  8         20  
101 17 100       55 $replaces->{$key}->{$inner} = delete $replaces->{$inner}
102             if $replaces->{$key}->{$inner} =~ m/=/;
103             }
104             }
105 4         11 for my $key (keys %$master) {
106 16 100       42 $master->{$key} = $replaces->{$key}
107             if $master->{$key} =~ m/=/;
108             }
109 4         19 return $master;
110             }
111              
112             sub deJSON {
113 4     4 0 1731 my ($self, $json)= @_;
114 4         11 return $self->_structure($json);
115             }
116              
117             =head1 BUGS
118              
119             Yup.
120              
121             It doesn't cope if you have curly braces in your strings. The next version
122             will cope with that, honest.
123              
124             I tried using Text::Balanced, but it didn't do what I wanted, so I rolled my
125             own. Yes, I know there are better ways to do it, but I wrote it without
126             access to the interwebs to find out how to better solve this solved problem.
127             Leave me alone, alright?
128              
129             =head1 AUTHOR
130              
131             Stray Taoist EFE
132              
133             =head1 COPYRIGHT
134              
135             Copyright (c) 2007 StrayTaoist
136              
137             This module is free software; you can redistribute it or modify it
138             under the same terms as Perl itself.
139              
140             =head1 STUFF
141              
142             o things
143              
144             =head1 THINGS
145              
146             o stuff
147              
148             =cut
149              
150             return qw/You drink your kawfee and I sip my tay/;