tags:

views:

590

answers:

2

I have a YAML document like this:

---
version: 1
rootdirectory:
  - subdirectory:
    - file1
    - file2
  - subdirectory2

that I am loading into a YAML::Tiny object like this:

$configuration = YAML::Tiny->read($configuration_file)

I see from invoking the script with the Perl debugger that what I end up with is a set of nested hashes and arrays that reflect the structure of the YAML source:

0  YAML::Tiny=ARRAY(0x84e3ba4)
   0  HASH(0x80627dc)
      'rootdirectory' => ARRAY(0x84e3d3c)
         0  HASH(0x84352b0)
            'subdirectory' => ARRAY(0x84e3df0)
               0  'file1'
               1  'file2'
         1  'subdirectory2'
      'version' => 1

So, I can do things like the following without problem:

print $configuration->[0]->{version}

and receive the expected answer of '1'. Likewise, I can also do:

print $configuration->[0]->{rootdirectory}->[0]->{subdirectory}->[0]

and receive the expected answer of 'file1'.

My problem comes from all of the a priori knowledge I need to derive my filename in the above example. As I am trying to allow the users of my script to describe an arbitrary directory structure in the YAML configuration this isn't good enough. I need to be able to "walk" the tree from 'rootdirectory'.

So, I would have imagined I could have done something like this:

print keys($configuration->[0])

which I would have expected to have returned 'rootdirectory,version' ... and so on, iterating over the arrays and hashes until I had walked the tree.

When I attempt to run the above example, I get:

Type of arg 1 to keys must be hash (not array element)

At this point I am stuck. As I understand it, $configuration->[0], whilst being an element of an array, is an element containing a hash that I believe I should be able to invoke the keys function against.

What am I missing here?

+2  A: 

Try

print "$_\n" for keys %{ $configuration->[0] };

You have to force the array element "$configuration->[0]" into a hash ref with "%{...}"

So to iterate over them:

for my $key ( keys %{ $configuration->[0] } ){
  my $value = $configuration->[0]{$key};

  print "$key => $value\n";
}
Brad Gilbert
Excellent, thankyou!As I understand this I basically need some "syntactic sugar" in the form of %{$configuration->[0]} to force Perl into believing that $configuration->[0] is actually a hash and not an array element that happens to contain a hash.Is that a fair comment?
sam_pointer
Yes ​
Brad Gilbert
The keyword `keys()` is defined like `keys(\%){...}` which forces you to use a hash, but doesn't auto-dereference for you.
Brad Gilbert
Excellent. Thanks again for your help.
sam_pointer
A: 

YAML::Tiny creates a rather complex data structure:

a reference to an array full of references to hashes full of references to arrays full of references to hashes ....

when $a is a reference to an array, you access the whole array as @$a and the elements as $$a[0], $$a[1], ... or alternatively as $a->[0], $a->[1]

when $b is a referenc to a hash, you access the whole hash as %$b and the elements as $$b{'somekey'}, $$b{'somekey'},.... or alternatively as $b->{'somekey'}, $b->{'somekey'}

when $configuration is a reference to an array full of references to hashes you access the whole array as @$configuration, the first element as $configuration->[0]. You could just copy that to another variable

$firstconfig = $configuration->[0]

and then access the whole hash as

%$firstconfig

but if you want to do it in one go you have to write the rather tricky

%{ $configuration->[0] }

I think this is one of the things that has been removed for Perl 6.

bjelli
Some of that has *changed*, but some of it still exists in Perl6.
Brad Gilbert
YAML::Tiny just rebuilds the serialized data structure. The source of the complexity is the YAML document being parsed.
Michael Carman