thoellri's Journal thoellri's use Perl Journal en-us use Perl; is Copyright 1998-2006, Chris Nandor. Stories, comments, journals, and other submissions posted on use Perl; are Copyright their respective owners. 2012-01-25T02:47:41+00:00 pudge Technology hourly 1 1970-01-01T00:00+00:00 thoellri's Journal Foundation vs. Mac::Propertylist <tt>I needed to access iPhoto's AlbumData.xml file in order to read my iPhoto database.<br><br>Mac::iPhoto did not work for me it always failed loading the xml-file (now I know why).<br><br>Mac::PropertyList would work after massaging the XML data before handing it of to plist_parse. However the solution was painfully slow. My 600KB AlbumData.xml file took more than 30 secs to load and parse on a Mac Mini.<br><br>So I looked into other ways to load the data from AlbumData.xml. Via PerlObjCBridge I implemented some code using NSPropertyListSerialization. I'm happy to report that the 30 secs have turned into 3 secs. That's better<nobr> <wbr></nobr>...<br><br>The data returned from plistToHash and loadiPhotoDB are different! There are some data types missing in the plistTraverse() sub!<br><br>--------------------------------------------------<br>use strict;<br>use Foundation;<br>use Mac::PropertyList;<br>use Time::HiRes qw{gettimeofday tv_interval};<br><br>use constant XML =&gt; qq{$ENV{HOME}/Pictures/iPhoto Library/AlbumData.xml};<br><br>my $t0 = [gettimeofday];<br>my $hash=plistToHash(XML);<br>my $elapsed = tv_interval($t0);<br>print "using plistToHash = $elapsed\n";<br>$t0 = [gettimeofday];<br>$hash=loadiPhotoDB(XML);<br>$elapsed = tv_interval($t0);<br>print "using Mac::PropertyList = $elapsed\n";<br><br>sub plistToHash {<br>&nbsp; &nbsp; my($filename)=@_;<br>&nbsp; &nbsp; my $data=NSData-&gt;dataWithContentsOfFile_($filename);<br>&nbsp; &nbsp; return undef unless($data);<br>&nbsp; &nbsp; my $plist=NSPropertyListSerialization-&gt;propertyListFromData_mutabilityOption_forma<nobr>t<wbr></nobr> _errorDescription_($data,0,undef,undef);<br>&nbsp; &nbsp; return undef unless($plist);<br>&nbsp; &nbsp; my %dict=();<br>&nbsp; &nbsp; return plistTraverse(\%dict,$plist,'dict',0);<br>}<br><br>sub plistTraverse {<br>&nbsp; &nbsp; my($dest,$src,$type,$depth)=@_;<br>&nbsp; &nbsp; my $e=($type eq 'dict')?$src-&gt;keyEnumerator():$src-&gt;objectEnumerator;<br>&nbsp; &nbsp; while(my $next = $e-&gt;nextObject()) {<br>&nbsp; &nbsp; last unless($$next);<br>&nbsp; &nbsp; my $obj=($type eq 'dict')?$src-&gt;objectForKey_($next):$next;<br>&nbsp; &nbsp; my $class=$obj-&gt;className-&gt;cString();<br>&nbsp; &nbsp; my $keyString=($type eq 'dict')?$next-&gt;cString:"";<br>&nbsp; &nbsp; if($class =~<nobr> <wbr></nobr>/dictionary$/i){<br>&nbsp; &nbsp; &nbsp; &nbsp; my %dict=();<br>&nbsp; &nbsp; &nbsp; &nbsp; my $sub=plistTraverse(\%dict,$obj,'dict',$depth+1);<br>&nbsp; &nbsp; &nbsp; &nbsp; if($type eq 'dict') {<br>&nbsp; &nbsp; &nbsp; &nbsp; $dest-&gt;{$keyString}=$sub;<br>&nbsp; &nbsp; &nbsp; &nbsp; }else{<br>&nbsp; &nbsp; &nbsp; &nbsp; push(@$dest,$sub);<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br>&nbsp; &nbsp; }elsif($class =~<nobr> <wbr></nobr>/array$/i){<br>&nbsp; &nbsp; &nbsp; &nbsp; my @array=();<br>&nbsp; &nbsp; &nbsp; &nbsp; my $sub=plistTraverse(\@array,$obj,'array',$depth+1);<br>&nbsp; &nbsp; &nbsp; &nbsp; if($type eq 'dict') {<br>&nbsp; &nbsp; &nbsp; &nbsp; $dest-&gt;{$keyString}=$sub;<br>&nbsp; &nbsp; &nbsp; &nbsp; }else{<br>&nbsp; &nbsp; &nbsp; &nbsp; push(@$dest,$sub);<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br>&nbsp; &nbsp; }elsif($class =~<nobr> <wbr></nobr>/string$/i){<br>&nbsp; &nbsp; &nbsp; &nbsp; if($type eq 'dict') {<br>&nbsp; &nbsp; &nbsp; &nbsp; $dest-&gt;{$keyString}=$obj-&gt;cString;<br>&nbsp; &nbsp; &nbsp; &nbsp; } else {<br>&nbsp; &nbsp; &nbsp; &nbsp; push(@$dest,$obj-&gt;cString);<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br>&nbsp; &nbsp; }elsif($class =~<nobr> <wbr></nobr>/number$/i){<br>&nbsp; &nbsp; &nbsp; &nbsp; if($type eq 'dict') {<br>&nbsp; &nbsp; &nbsp; &nbsp; $dest-&gt;{$keyString}=$obj-&gt;doubleValue;<br>&nbsp; &nbsp; &nbsp; &nbsp; } else {<br>&nbsp; &nbsp; &nbsp; &nbsp; push(@$dest,$obj-&gt;doubleValue);<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br>&nbsp; &nbsp; }elsif($class =~<nobr> <wbr></nobr>/boolean$/i){<br>&nbsp; &nbsp; &nbsp; &nbsp; if($type eq 'dict') {<br>&nbsp; &nbsp; &nbsp; &nbsp; $dest-&gt;{$keyString}=($obj-&gt;boolValue eq 'YES')?1:0;<br>&nbsp; &nbsp; &nbsp; &nbsp; } else {<br>&nbsp; &nbsp; &nbsp; &nbsp; push(@$dest,($obj-&gt;boolValue eq 'YES')?1:0);<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br>&nbsp; &nbsp; } else {<br>&nbsp; &nbsp; &nbsp; &nbsp; print STDERR "**** unhandled class: $class\n";<br>&nbsp; &nbsp; }<br>&nbsp; &nbsp; }<br>&nbsp; &nbsp; return $dest;<br>}<br><br>sub loadiPhotoDB {<br>&nbsp; &nbsp; my($catalogPath)=@_;<br>&nbsp; &nbsp; my $xml;<br>&nbsp; &nbsp; open(CATALOG, $catalogPath) || return undef;<br>&nbsp; &nbsp; {local $/=undef;$xml=&lt;CATALOG&gt;;}<br>&nbsp; &nbsp; close(CATALOG);<br><br>&nbsp; &nbsp; # Mac::PropertyList is pretty strict about what it expects to<br>&nbsp; &nbsp; # see in the XML file. We are trimming the file before handing<br>&nbsp; &nbsp; # it off to parse_plist<br>&nbsp; &nbsp; $xml =~ s{^.*&lt;plist\s*.*?&gt;\s*}{}s;<br>&nbsp; &nbsp; $xml =~ s{\s*&lt;/plist\s*.*?&gt;\s*\z}{}s;<br><br>&nbsp; &nbsp; my $dict=Mac::PropertyList::parse_plist($xml);<br>&nbsp; &nbsp; return $dict;<br>}<br><br></tt> thoellri 2005-06-11T12:20:35+00:00 journal