Here's a config file to copy MWG Keywords to HierarchicalSubject and to do the reverse. Inspired by this post (https://exiftool.org/forum/index.php/topic,9228.msg47676.html#msg47676) from @colink.
As usual with the stuff I do, it has had limited testing and there are probably some edge cases where it will fail (keywords with pipe characters come to mind, is a pipe character in a keyword legal in HierarchicalSubject?). But using the hierarchy mentioned in the MWG pdf (http://www.metadataworkinggroup.org/pdf/mwg_guidance.pdf) (with the addition of a keyword without a hierarchy), it seems to work. Any tweaks, suggestions, or examples of problems to be fixed are welcome.
HS is a shortcut I created in my config file for HierarchicalSubject, as I find it easier to type out and always forget how to spell Hierarchical.
To be honest, I have doubts this will get used as I don't know of any software that actually uses MWG Keywords. But it was a good learning experience and if someone does actually find it useful, all the better.
Example output:
C:\>exiftool -echo "List tags at start" -g1 -a -s -hs -XMP-mwg-kw:all y:\!temp\Test3.jpg
List tags at start
---- XMP-lr ----
HierarchicalSubject : No Hierarchy Keyword, Animals|Mammals|Dog, Animals|Mammals|Cat, People|Georgia, People|Cat, Places|States|Georgia, Places|States|Wyoming
C:\>exiftool -config MWGKeywordsConversion.config -echo "Copy HS to MWG" -P -overwrite_original "-KeywordInfo<HSToMWGKeywords" y:\!temp\Test3.jpg
Copy HS to MWG
1 image files updated
C:\>exiftool -echo "List tags after copy" -g1 -a -s -hs -XMP-mwg-kw:all y:\!temp\Test3.jpg
List tags after copy
---- XMP-lr ----
HierarchicalSubject : No Hierarchy Keyword, Animals|Mammals|Dog, Animals|Mammals|Cat, People|Georgia, People|Cat, Places|States|Georgia, Places|States|Wyoming
---- XMP-mwg-kw ----
HierarchicalKeywords3 : Dog, Cat, Georgia, Wyoming
HierarchicalKeywords2 : Mammals, States, Cat, Georgia
HierarchicalKeywords1 : Animals, Places, People, No Hierarchy Keyword
C:\>exiftool -echo "List Struct after copy" -g1 -a -s -hs -XMP-mwg-kw:all -struct y:\!temp\Test3.jpg
List Struct after copy
---- XMP-lr ----
HierarchicalSubject : [No Hierarchy Keyword,Animals||Mammals||Dog,Animals||Mammals||Cat,People||Georgia,People||Cat,Places||States||Georgia,Places||States||Wyoming]
---- XMP-mwg-kw ----
KeywordInfo : {Hierarchy=[{Children=[{Children=[{Keyword=Dog},{Keyword=Cat}],Keyword=Mammals}],Keyword=Animals},{Children=[{Children=[{Keyword=Georgia},{Keyword=Wyoming}],Keyword=States}],Keyword=Places},{Children=[{Keyword=Cat},{Keyword=Georgia}],Keyword=People},{Keyword=No Hierarchy Keyword}]}
C:\>exiftool -echo "Clear HierarchicalSubject" -P -overwrite_original -hs= y:\!temp\Test3.jpg
Clear HierarchicalSubject
1 image files updated
C:\>exiftool -config MWGKeywordsConversion.config -echo "Copy MWG Keywords to HierarchicalSubject" -P -overwrite_original "-HierarchicalSubject<MWGKeywordsToHS" y:\!temp\Test3.jpg
Copy MWG Keywords to HierarchicalSubject
1 image files updated
C:\>exiftool -echo "List tags at end" -g1 -a -s -hs -XMP-mwg-kw:all y:\!temp\Test3.jpg
List tags at end
---- XMP-lr ----
HierarchicalSubject : Animals|Mammals|Dog, Animals|Mammals|Cat, Places|States|Georgia, Places|States|Wyoming, People|Cat, People|Georgia, No Hierarchy Keyword
---- XMP-mwg-kw ----
HierarchicalKeywords3 : Dog, Cat, Georgia, Wyoming
HierarchicalKeywords2 : Mammals, States, Cat, Georgia
HierarchicalKeywords1 : Animals, Places, People, No Hierarchy Keyword
Config file (also attached at end):
#------------------------------------------------------------------------------
# File: MWGKeywordsConversion.config
#
# Description: User-defined Composite tag definitions to convert MWG:Keywords
# to HierarchicalSubject and HierarchicalSubject to MWG:Keywords.
#
# Tag definitions and examples:
#
# MWGKeywordsToHS
# Creates a HierarchicalSubject List from a MWG KeywordInfo structure that
# can be copied to HierarchicalSubject.
#
# Example:
# exiftool -config MWGKeywordsConversion.config "-HierarchicalSubject<MWGKeywordsToHS" FilesOrDirs
#
# HSToMWGKeywords
# Creates a MWG KeywordInfo structure from HierarchicalSubject that
# can be copied to KeywordInfo.
#
# Example:
# exiftool -config MWGKeywordsConversion.config "-KeywordInfo<HSToMWGKeywords" FilesOrDirs
#
# Revisions: 2018/05/28 - Bryan K. Williams (aka StarGeek) Created
#------------------------------------------------------------------------------
# Main recursive array to convert MWG:Keywords to HierarchicalSubject
sub RecurseArray{
# $_ is array ref
($_)=@_;
my $tempKeyword='';
my %a;
my @Return_Array=();
foreach $a (@$_){
$tempKeyword=%$a{'Keyword'};
if (defined %$a{'Children'}) {
my $arr_ref = %$a{'Children'};
my @x = map{ $_= $tempKeyword.'|'.$_ } RecurseArray($arr_ref);
push @Return_Array, @x;
}
else {
push @Return_Array, $tempKeyword;
}
}
return (@Return_Array);
}
# Builds Hierarchy hash out of Hierarchical Subject
# Easier than trying to add each section of all the
# HierarchicalSubject entries individually
# Blatently stolen from this StackOverflow answer
# https://stackoverflow.com/a/4559059/3525475
sub BuildTree{
my $hash = shift;
return unless @_;
return BuildTree($hash->{shift()} //= {}, @_);
}
# Builds MWGKeywords HashRefKey
# Input: Hash tree result from BuildTree, Array Reference
sub RecurseHS{
my $hashref = shift;
my $ArrRef = shift;
foreach my $key (keys %$hashref){
my @TempArr;
# Check if there are children for this section of the tree
# If there are, recurse then add Keyword value and Children array
# else add just Keyword value.
if (%{$hashref->{$key}}) {
RecurseHS($hashref->{$key},\@TempArr);
push @{$ArrRef}, { 'Keyword' => $key, 'Children'=>\@TempArr} ;
}
else{
push @{$ArrRef}, { 'Keyword' => $key} ;
}
}
return
}
%Image::ExifTool::UserDefined = (
'Image::ExifTool::Composite' => {
MWGKeywordsToHS =>{
Require => { 0 => 'KeywordInfo'},
ValueConv => sub {
my ($val, $et) = @_;
my $MainArray_Ref = (${@$val[0]}{'Hierarchy'});
my @A = RecurseArray($MainArray_Ref);
return @A ? \@A : undef;
},
},
HSToMWGKeywords => {
Require => 'HierarchicalSubject',
ValueConv => sub{
my ($val, $et) = @_;
my %HashTree;
my $HSArray_Ref =$val->[0];
foreach my $HS (@{$HSArray_Ref}){
BuildTree(\%HashTree,split(m!\|!,$HS));
}
my $hashref = \%HashTree;
my @HierArr;
RecurseHS($hashref,\@HierArr);
my %KeywordInfo;
$KeywordInfo{'Hierarchy'}=\@HierArr;
return \%KeywordInfo;
},
},
########
},
);
1; #end