| 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__ |