File Coverage

blib/lib/Image/ExifTool/JSON.pm
Criterion Covered Total %
statement 62 64 96.8
branch 27 40 67.5
condition 12 27 44.4
subroutine 7 7 100.0
pod 0 3 0.0
total 108 141 76.6


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: JSON.pm
3             #
4             # Description: Read JSON files
5             #
6             # Notes: Set ExifTool MissingTagValue to "null" to ignore JSON nulls
7             #
8             # Revisions: 2017/03/13 - P. Harvey Created
9             #------------------------------------------------------------------------------
10              
11             package Image::ExifTool::JSON;
12 5     5   3489 use strict;
  5         13  
  5         179  
13 5     5   23 use vars qw($VERSION);
  5         11  
  5         229  
14 5     5   29 use Image::ExifTool qw(:DataAccess :Utils);
  5         10  
  5         1104  
15 5     5   2560 use Image::ExifTool::Import;
  5         10  
  5         3997  
16              
17             $VERSION = '1.05';
18              
19             sub ProcessJSON($$);
20             sub ProcessTag($$$$%);
21              
22             %Image::ExifTool::JSON::Main = (
23             GROUPS => { 0 => 'JSON', 1 => 'JSON', 2 => 'Other' },
24             VARS => { NO_ID => 1 },
25             PROCESS_PROC => \&ProcessJSON,
26             NOTES => q{
27             Other than a few tags in the table below, JSON tags have not been
28             pre-defined. However, ExifTool will read any existing tags from basic
29             JSON-formatted files.
30             },
31             # ON1 settings tags
32             ON1_SettingsData => {
33             RawConv => q{
34             require Image::ExifTool::XMP;
35             $val = Image::ExifTool::XMP::DecodeBase64($val);
36             },
37             SubDirectory => { TagTable => 'Image::ExifTool::PLIST::Main' },
38             },
39             ON1_SettingsMetadataCreated => { Groups => { 2 => 'Time' } },
40             ON1_SettingsMetadataModified => { Groups => { 2 => 'Time' } },
41             ON1_SettingsMetadataName => { },
42             ON1_SettingsMetadataPluginID => { },
43             ON1_SettingsMetadataTimestamp => { Groups => { 2 => 'Time' } },
44             ON1_SettingsMetadataUsage => { },
45             ON1_SettingsMetadataVisibleToUser=>{ },
46             );
47              
48             #------------------------------------------------------------------------------
49             # Store a tag value
50             # Inputs: 0) ExifTool ref, 1) tag table, 2) tag ID, 3) value, 4) tagInfo flags
51             sub FoundTag($$$$%)
52             {
53 74     74 0 139 my ($et, $tagTablePtr, $tag, $val, %flags) = @_;
54              
55             # special case to reformat ON1 tag names
56 74 50       175 if ($tag =~ s/^settings\w{8}-\w{4}-\w{4}-\w{4}-\w{12}(Data|Metadata.+)$/ON1_Settings$1/) {
57 0 0       0 $et->OverrideFileType('ONP','application/on1') if $$et{FILE_TYPE} eq 'JSON';
58             }
59              
60             # avoid conflict with special table entries
61 74 50       170 $tag .= '!' if $Image::ExifTool::specialTags{$tag};
62              
63             AddTagToTable($tagTablePtr, $tag, {
64             Name => Image::ExifTool::MakeTagName($tag),
65             %flags,
66             Temporary => 1,
67 74 100       307 }) unless $$tagTablePtr{$tag};
68              
69 74         224 $et->HandleTag($tagTablePtr, $tag, $val);
70             }
71              
72             #------------------------------------------------------------------------------
73             # Process a JSON tag
74             # Inputs: 0) ExifTool ref, 1) tag table, 2) tag ID, 3) value, 4) tagInfo flags
75             # - expands structures into flattened tags as required
76             sub ProcessTag($$$$%)
77             {
78 85     85 0 122 local $_;
79 85         187 my ($et, $tagTablePtr, $tag, $val, %flags) = @_;
80              
81 85 100       298 if (ref $val eq 'HASH') {
    100          
    100          
82 2 50       6 if ($et->Options('Struct')) {
83 2         9 FoundTag($et, $tagTablePtr, $tag, $val, %flags, Struct => 1);
84 2 50       19 return unless $et->Options('Struct') > 1;
85             }
86             # support hashes with ordered keys
87 2 50       22 my @keys = $$val{_ordered_keys_} ? @{$$val{_ordered_keys_}} : sort keys %$val;
  0         0  
88 2         5 foreach (@keys) {
89 4 50 33     17 my $tg = $tag . ((/^\d/ and $tag =~ /\d$/) ? '_' : '') . ucfirst;
90 4         10 $tg =~ s/([^a-zA-Z])([a-z])/$1\U$2/g;
91 4         649 ProcessTag($et, $tagTablePtr, $tg, $$val{$_}, %flags, Flat => 1);
92             }
93             } elsif (ref $val eq 'ARRAY') {
94 8         14 foreach (@$val) {
95 13         45 ProcessTag($et, $tagTablePtr, $tag, $_, %flags, List => 1);
96             }
97             } elsif (defined $val) {
98 72         174 FoundTag($et, $tagTablePtr, $tag, $val, %flags);
99             }
100             }
101              
102             #------------------------------------------------------------------------------
103             # Extract meta information from a JSON file
104             # Inputs: 0) ExifTool object reference, 1) dirInfo reference
105             # Returns: 1 on success, 0 if this wasn't a recognized JSON file
106             sub ProcessJSON($$)
107             {
108 58     58 0 100 local $_;
109 58         122 my ($et, $dirInfo) = @_;
110 58         107 my $raf = $$dirInfo{RAF};
111 58         194 my $structOpt = $et->Options('Struct');
112 58         122 my (%database, $key, $tag, $dataPt);
113              
114 58 100       136 unless ($raf) {
115 57         95 $dataPt = $$dirInfo{DataPt};
116 57 50 0     161 if ($$dirInfo{DirStart} or ($$dirInfo{DirLen} and $$dirInfo{DirLen} ne length($$dataPt))) {
      33        
117 57         79 my $buff = substr(${$$dirInfo{DataPt}}, $$dirInfo{DirStart}, $$dirInfo{DirLen});
  57         156  
118 57         115 $dataPt = \$buff;
119             }
120 57         230 $raf = new File::RandomAccess($dataPt);
121             # extract as a block if requested
122 57 50       169 my $blockName = $$dirInfo{BlockInfo} ? $$dirInfo{BlockInfo}{Name} : '';
123 57         141 my $blockExtract = $et->Options('BlockExtract');
124 57 100 66     510 if ($blockName and ($blockExtract or $$et{REQ_TAG_LOOKUP}{lc $blockName} or
      66        
125             ($$et{TAGS_FROM_FILE} and not $$et{EXCL_TAG_LOOKUP}{lc $blockName})))
126             {
127 15         55 $et->FoundTag($$dirInfo{BlockInfo}, $$dataPt);
128 15 50 33     54 return 1 if $blockExtract and $blockExtract > 1;
129             }
130 57         172 $et->VerboseDir('JSON');
131             }
132              
133             # read information from JSON file into database structure
134 58         158 my $err = Image::ExifTool::Import::ReadJSON($raf, \%database,
135             $et->Options('MissingTagValue'), $et->Options('Charset'));
136              
137 58 50 33     247 return 0 if $err or not %database;
138              
139 58 100       153 $et->SetFileType() unless $dataPt;
140              
141 58         157 my $tagTablePtr = GetTagTable('Image::ExifTool::JSON::Main');
142              
143             # remove any old tag definitions in case they change flags
144 58         171 foreach $key (TagTableKeys($tagTablePtr)) {
145 517 100       1002 delete $$tagTablePtr{$key} if $$tagTablePtr{$key}{Temporary};
146             }
147              
148             # extract tags from JSON database
149 58         188 foreach $key (sort keys %database) {
150 58         91 foreach $tag (sort keys %{$database{$key}}) {
  58         196  
151 126         217 my $val = $database{$key}{$tag};
152             # (ignore SourceFile if generated automatically by ReadJSON)
153 126 50 66     480 next if $tag eq 'SourceFile' and defined $val and $val eq '*';
      66        
154 68         172 ProcessTag($et, $tagTablePtr, $tag, $val);
155             }
156             }
157 58         280 return 1;
158             }
159              
160             1; # end
161              
162             __END__