File Coverage

blib/lib/JSON/WithComments.pm
Criterion Covered Total %
statement 39 49 79.5
branch 7 14 50.0
condition 0 2 0.0
subroutine 11 11 100.0
pod 0 4 0.0
total 57 80 71.2


line stmt bran cond sub pod time code
1             ###############################################################################
2             #
3             # This file copyright © 2017 by Randy J. Ray, all rights reserved
4             #
5             # See "LICENSE AND COPYRIGHT" in the POD for terms.
6             #
7             ###############################################################################
8             #
9             # Description: Simple support for comments in JSON content.
10             #
11             # Functions: import
12             # comment_style
13             # get_comment_style
14             # decode
15             #
16             # Libraries: JSON
17             #
18             # Global Consts: %PATTERNS
19             #
20             # Environment: None
21             #
22             ###############################################################################
23              
24             package JSON::WithComments;
25              
26 2     2   93816 use 5.008;
  2         7  
27 2     2   11 use strict;
  2         5  
  2         57  
28 2     2   12 use warnings;
  2         8  
  2         66  
29 2     2   12 use base qw(JSON);
  2         9  
  2         873  
30              
31 2     2   17218 use Carp ();
  2         4  
  2         806  
32              
33             our $VERSION = '0.003'; # VERSION
34              
35             # These regular expressions are adapted from Regexp::Common::comment.
36              
37             # The length of the regexp for JS multi-line comments triggers this:
38             ## no critic(RegularExpressions::RequireExtendedFormatting)
39             my $JS_SINGLE = qr{(?://)(?:[^\n]*)};
40             my $JS_MULTI = qr{(?:\/[*])(?:(?:[^*]+|[*](?!\/))*)(?:[*]\/)};
41             my $PERL = qr{(?:#)(?:[^\n]*)};
42             my %PATTERNS = (
43             perl => qr{(?
44             javascript => qr{(?
45             );
46              
47             # This is the comment-style that will be used if/when an object has not
48             # specified a style. It can be changed in import() with -default_comment_style.
49             # This is also the style that will be used by decode_json.
50             my $default_comment_style = 'javascript';
51              
52             # This table is used in lieu of per-object hashkeys, as the object is not a
53             # hashref when the JSON::XS backend is in use.
54             my %comment_style;
55              
56             sub import {
57 2     2   17 my ($class, @imports) = @_;
58              
59 2         4 my ($index, $style);
60 2         7 for my $idx (0 .. $#imports) {
61 0 0       0 if ($imports[$idx] eq '-default_comment_style') {
62 0         0 $index = $idx;
63 0         0 $style = $imports[$idx + 1];
64 0         0 last;
65             }
66             }
67 2 50       9 if (defined $index) {
68 0   0     0 $style ||= '(undef)';
69 0 0       0 if (! $PATTERNS{$style}) {
70 0         0 Carp::croak "Unknown comment style '$style' given as default";
71             }
72 0         0 $default_comment_style = $style;
73 0         0 splice @imports, $index, 2;
74             }
75              
76 2         28 return $class->SUPER::import(@imports);
77             }
78              
79             sub new {
80 2     2 0 1940 my $class = shift;
81              
82 2         30 return $class->SUPER::new->comment_style($default_comment_style);
83             }
84              
85             sub comment_style {
86 4     4 0 1808 my ($self, $value) = @_;
87              
88 4 50       12 if (defined $value) {
89 4 100       16 if ($value eq 'perl') {
    50          
90 2         8 $self->relaxed(1);
91             } elsif ($PATTERNS{$value}) {
92 2         15 $self->relaxed(0);
93             } else {
94 0         0 Carp::croak "Unknown comment_style ($value)";
95             }
96 4         23 $comment_style{"$self"} = $value;
97             }
98              
99 4         13 return $self;
100             }
101              
102             sub get_comment_style {
103 6     6 0 395 my $self = shift;
104              
105 6         32 return $comment_style{"$self"};
106             }
107              
108             sub decode {
109 4     4 0 194 my ($self, $text) = @_;
110              
111             # Perl-style comments are handled by having set the "relaxed" property on
112             # the object. We only use the regexp approach for the other style(s).
113 4 100       12 if ((my $style = $self->get_comment_style) ne 'perl') {
114 2         4 my $comment_re = $PATTERNS{$style};
115             # The JSON module reports errors using the character-offset within the
116             # string as a whole. So rather than deleting comments, replace them
117             # with a string of spaces of the same length. This should mean that any
118             # reported character offsets in the JSON data will still be correct.
119 2         28 $text =~ s/$comment_re/q{ } x length($1)/ge;
  5         42  
120             }
121              
122 4         52 return $self->SUPER::decode($text);
123             }
124              
125             sub DESTROY {
126 2     2   964 my $self = shift;
127              
128 2         8 delete $comment_style{"$self"};
129              
130 2         80 return;
131             }
132              
133             1;
134              
135             __END__