macdevcenter.com
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Using Perl to Manage Plist Files
Pages: 1, 2, 3, 4, 5

Getting Nested Plist Values

If the object returned from your first read attempt is a dictionary, you continue to use objectForKey_() on the results to read the nested dictionary. If the object returned is an array, you read it by using objectAtIndex_(). If you use objectForKey_() on an array, or objectAtIndex_() on a dictionary, you will get an error. Try this code:


#!/usr/bin/perl

use Foundation;
use lib "/Users/yourname/Desktop/";
require "perlplist.pl"; # for perlValue

$file = "/Library/Preferences/SystemConfiguration/preferences.plist";
$plist = NSDictionary->dictionaryWithContentsOfFile_( $file );
$value = perlValue( $plist->objectAtIndex_(1) );

If you get something like this error:

Can't locate perlplist.pl in @INC (@INC contains: /Users/yourname/Desktop/ ...snip... at test line 5.

That means your library file was not found. Make sure the path to your library file is correct.

You should get this error by calling a method that doesn't exist in the object:

2005-07-22 00:46:33.619 perl[2076] *** -[NSCFDictionary objectAtIndex:]: selector not recognized [self = 0x304fe0] ...snip... **** PerlObjCBridge: dying due to NSException

To avoid this, you can check the type of object returned by using isKindOfClass_() and class.


#!/usr/bin/perl

use Foundation;
use lib "/Users/yourname/Desktop/";
require "perlplist.pl"; # for perlValue

$file = "/Library/Preferences/SystemConfiguration/preferences.plist";
$plist = NSDictionary->dictionaryWithContentsOfFile_( $file );
$object = $plist->objectForKey_("System");
if ( $object->isKindOfClass_( NSArray->class ) ) {

  print "$object is a array\n";

  # if we want to read it, we use:
  $object = $plist->objectAtIndex_( $index );

} elsif ( $object->isKindOfClass_( NSDictionary->class ) ) {

  print "$object is a dictionary\n";

  # if we want to read it, we use:
  $ object = $plist->objectForKey_( $key );

} else {

  print "$object is something else, print its value:\n";
  print perlValue( $object ) . "\n";

}

After running the above example, you should get:


NSCFDictionary=SCALAR(0x180eaa0) is a dictionary

If you ask for an index or a key that does not exist, you will get back nil. So you will always want to test the return value for both a Cocoa error and nil:


$file = "/Library/Preferences/SystemConfiguration/preferences.plist";
$plist = NSDictionary->dictionaryWithContentsOfFile_( $file );
$object = $plist->objectForKey_("some key");
if ( ! $object or ! $$object ) {
  print "Object did not exist\n";
}

Using everything shown up to this point, we can actually write a subroutine that will get nested objects for us. The following subroutine takes an array or dictionary, and then a list of keys or indexes to read. It will traverse the array or dictionary given to it, using the keys or indexes, until it reaches the last one. At that point, it returns either the object found; nil, if it could not find what you asked for; or nothing, if there was another problem. Here is the subroutine with an example reading a nested object. You may want to add this subroutine to your library file. It will be used in the second article.


#!/usr/bin/perl

use Foundation;
use lib "/Users/yourname/Desktop/";
require "perlplist.pl"; # for perlValue

sub getPlistObject {
  my ( $object, @keysIndexes ) = ( @_ );
  if ( @keysIndexes ) {
    foreach my $keyIndex ( @keysIndexes ) {
      if ( $object and $$object ) {
        if ( $object->isKindOfClass_( NSArray->class ) ) {
          $object = $object->objectAtIndex_( $keyIndex );
        } elsif ( $object->isKindOfClass_( NSDictionary->class ) ) {
          $object = $object->objectForKey_( $keyIndex );
        } else {
          print STDERR "Unknown type (not an array or a dictionary):\n";
          return;
        }
      } else {
        print STDERR "Got nil or other error for $keyIndex.\n";
        return;
      }
    }
  }
  return $object;
}

$file = "/Library/Preferences/SystemConfiguration/preferences.plist";
$plist = NSDictionary->dictionaryWithContentsOfFile_( $file );
if ( $plist and $$plist) {

  $computerName = getPlistObject( $plist, "System", "System", "ComputerName" );
  if ( $computerName and $$computerName ) {

    print perlValue( $computerName ) . "\n";

  } else {
    die "Could not find the value.\n";
  }
} else {
  die "Error loading file.\n";
}

The $plist variable holds the location to the dictionary loaded with the computer network settings, which is a very large file. Using the getPlistObject subroutine, these are the only lines in $plist that we care about:


<dict>
	<key>System</key>
	<dict>
		<key>System</key>
		<dict>
			<key>ComputerName</key>
			<string>Your computer name</string>
		</dict>
	</dict>
</dict>

After calling getPlistObject, $computerName will point to an NSString with the value of your computer's name.

Final Thoughts

This article has covered a lot of ground. If you are new to Cocoa, your head might be spinning with all of the object-oriented jargon. However, we have only touched the ground as far as managing Plist files. The best is yet to come! The second article will cover changing Plist values, saving the changes, scanning Plist files, and creating them from scratch. Do stay tuned in!

James Reynolds is a member of the University of Utah's Student Computing Labs Mac Group. His main duty is the deployment of Mac OS X. Most of his responsibilities include the OS customizations, scripts, and security of the Mac OS X lab and kiosk computers supported by SCL.


Return to the Mac DevCenter