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   4639 use strict;
  5         13  
  5         185  
13 5     5   29 use vars qw($VERSION);
  5         19  
  5         264  
14 5     5   35 use Image::ExifTool qw(:DataAccess :Utils);
  5         12  
  5         1197  
15 5     5   3253 use Image::ExifTool::Import;
  5         16  
  5         4724  
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 176 my ($et, $tagTablePtr, $tag, $val, %flags) = @_;
54              
55             # special case to reformat ON1 tag names
56 74 50       200 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       198 $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       368 }) unless $$tagTablePtr{$tag};
68              
69 74         283 $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 144 local $_;
79 85         226 my ($et, $tagTablePtr, $tag, $val, %flags) = @_;
80              
81 85 100       355 if (ref $val eq 'HASH') {
    100          
    100          
82 2 50       9 if ($et->Options('Struct')) {
83 2         8 FoundTag($et, $tagTablePtr, $tag, $val, %flags, Struct => 1);
84 2 50       16 return unless $et->Options('Struct') > 1;
85             }
86             # support hashes with ordered keys
87 2 50       14 my @keys = $$val{_ordered_keys_} ? @{$$val{_ordered_keys_}} : sort keys %$val;
  0         0  
88 2         5 foreach (@keys) {
89 4 50 33     29 my $tg = $tag . ((/^\d/ and $tag =~ /\d$/) ? '_' : '') . ucfirst;
90 4         9 $tg =~ s/([^a-zA-Z])([a-z])/$1\U$2/g;
91 4         16 ProcessTag($et, $tagTablePtr, $tg, $$val{$_}, %flags, Flat => 1);
92             }
93             } elsif (ref $val eq 'ARRAY') {
94 8         20 foreach (@$val) {
95 13         42 ProcessTag($et, $tagTablePtr, $tag, $_, %flags, List => 1);
96             }
97             } elsif (defined $val) {
98 72         218 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 127 local $_;
109 58         142 my ($et, $dirInfo) = @_;
110 58         124 my $raf = $$dirInfo{RAF};
111 58         190 my $structOpt = $et->Options('Struct');
112 58         181 my (%database, $key, $tag, $dataPt);
113              
114 58 100       155 unless ($raf) {
115 57         114 $dataPt = $$dirInfo{DataPt};
116 57 50 0     168 if ($$dirInfo{DirStart} or ($$dirInfo{DirLen} and $$dirInfo{DirLen} ne length($$dataPt))) {
      33        
117 57         93 my $buff = substr(${$$dirInfo{DataPt}}, $$dirInfo{DirStart}, $$dirInfo{DirLen});
  57         192  
118 57         119 $dataPt = \$buff;
119             }
120 57         267 $raf = new File::RandomAccess($dataPt);
121             # extract as a block if requested
122 57 50       194 my $blockName = $$dirInfo{BlockInfo} ? $$dirInfo{BlockInfo}{Name} : '';
123 57         160 my $blockExtract = $et->Options('BlockExtract');
124 57 100 66     591 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         72 $et->FoundTag($$dirInfo{BlockInfo}, $$dataPt);
128 15 50 33     89 return 1 if $blockExtract and $blockExtract > 1;
129             }
130 57         198 $et->VerboseDir('JSON');
131             }
132              
133             # read information from JSON file into database structure
134 58         198 my $err = Image::ExifTool::Import::ReadJSON($raf, \%database,
135             $et->Options('MissingTagValue'), $et->Options('Charset'));
136              
137 58 50 33     289 return 0 if $err or not %database;
138              
139 58 100       154 $et->SetFileType() unless $dataPt;
140              
141 58         192 my $tagTablePtr = GetTagTable('Image::ExifTool::JSON::Main');
142              
143             # remove any old tag definitions in case they change flags
144 58         255 foreach $key (TagTableKeys($tagTablePtr)) {
145 517 100       1209 delete $$tagTablePtr{$key} if $$tagTablePtr{$key}{Temporary};
146             }
147              
148             # extract tags from JSON database
149 58         225 foreach $key (sort keys %database) {
150 58         115 foreach $tag (sort keys %{$database{$key}}) {
  58         211  
151 126         242 my $val = $database{$key}{$tag};
152             # (ignore SourceFile if generated automatically by ReadJSON)
153 126 50 66     561 next if $tag eq 'SourceFile' and defined $val and $val eq '*';
      66        
154 68         210 ProcessTag($et, $tagTablePtr, $tag, $val);
155             }
156             }
157 58         345 return 1;
158             }
159              
160             1; # end
161              
162             __END__