I have the following data I wish to access:
$data1=
{'Family' => {
'House' => [
{
'Id' => '1111',
'Name' => 'DFG'
},
{
'Id' => '211',
'Name' => 'ABC'
}
]
}
}
I want to access the each Name field value. I am using this code:
foreach(keys%$data1) {
if(ref($data1->{$_}) eq 'HASH') {
foreach my $inner_key (keys%{$data1->{$_}}) {
print "Key:$inner_key and value:$data1->{$_}->{$inner_key}\n";
}
}
else {
print "Key: $_ and Value: $data1->{$_}\n"
}
}
It prints Key:House and value:ARRAY(OXXX).
I know I am doing something wrong. Since the data in 'House' is an array of hashes, I even tried accessing through $data1->{$_}->{$inner_key}[0]. What is wrong in the code???
You have to dereference array for foreach loop first, and then dereference hashref to reach "Name" values.
print "Key:$inner_key and value:$_->{Name}\n"
for #{$data1->{$_}->{$inner_key}};
You should read perlref first to learn how to create and use references.
Here is a demonstration:
#!/usr/bin/perl
use strict;
use warnings;
my $data1=
{'Family' => {
'House' => [
{
'Id' => '1111',
'Name' => 'DFG'
},
{
'Id' => '211',
'Name' => 'ABC'
}
]
}
};
while (my ($key1, $val1) = each %$data1) {
print "\$key1 = $key1\n";
while (my ($key2, $val2) = each %$val1) {
print "\t\$key2 = $key2\n";
foreach my $val3 (#$val2) {
while (my ($key4, $val4) = each %$val3) {
print "\t\t\$key4 = $key4 => $val4\n";
}
print "\n";
}
}
}
[Edit I typed too slowly while answering, so this response bascially duplicates #mpapec's below - I will leave the references here and you can vote me up for those ;-) but do not accept my response as the answer].
Try something like the following to see if it works:
for $inner_hash (#{ $data1->{Family}{House} }) {
say "Name: $inner_hash->{Name}"
}
since you need to get the inner hashes' values from inside the elements of the array (that is what value:ARRAY(OXXX) is telling you).
You can use perldoc to look at the perldata, perlref, perlreftut and perldsc PODs to learn more about data structures and dereferencing. If keeping your data structure in mind while you are writing code gets to be too hard to do, it may mean you need to simplify things: either the data itself or by writing sub's to make it easier to access, or making use some of the excellent utility modules from CPAN.
There's also some good perl data structure related tutorials out there. The POD/perldoc documentation that ships with perl (along with Chapter 9 of Programming Perl) is the canonical reference, but you might browse these nodes from perlmonks:
References quick reference
Referencing in advanced data structures
Visualizing perl data structures
Perlmonks Hash Tutorial
NB Above I'm using the perlcritic and Perl Best Practices style of dereferencing: e.g.: #{ $data1->{Family}{House} } so the syntax reminds me that the inner hashes (or inner-inner?) are inside an array. There's a cool new way of dereferencing being introduced in perl 5.20 called postfix dereferencing which will be great, but you can't go wrong following the recommendations of PBP.
"Until you start thinking in terms of hashes, you aren't really thinking in Perl." -- Larry Wall
Cheers,
Related
I totally got this question wrong. Am using the method from TMDB:
my #results = $search->find(id => 'tt0114694', source => 'imdb_id');
I thought the output was in JSON format, so that's what confused me, which kept me running in circles because I was looking at it all wrong.
Didn't realize the data below, from Dumper, was the actual hashes the I had to go through.
This is where I am running into a wall, So the data below is a hash with five keys. The fifth key, the I want, contains another array. It is that array I cannot read into. I try dereferencing that into a hash, and that is where I fail.
The code I am trying is:
foreach my $narray (#results){
print $narray->{"movie_results"};
my #newarray = $narray->{"movie_results"};
foreach my $otherarray (#newarray){
my %innerhash = $otherarray;
print %innerhash;
print "\n";
}
}
It will print out an array, but I am unable to read the hash in that array.
p.s. I had to format this output as code, or else it came out with no line breaks.
$VAR1 = {
'tv_season_results' => [],
'tv_results' => [],
'person_results' => [],
'tv_episode_results' => [],
'movie_results' => [
{
'adult' => bless( do{\(my $o = 0)}, 'JSON::PP::Boolean' ),
'vote_average' => '6.8',
'original_title' => 'Tommy Boy',
'vote_count' => 635,
'id' => 11381,
'release_date' => '1995-03-31',
'overview' => 'Party animal Tommy Callahan is a few cans short of a six-pack. But when the family business starts tanking, it\'s up to Tommy and number-cruncher Richard Hayden to save the day.',
'genre_ids' => [
35
],
'title' => 'Tommy Boy',
'video' => $VAR1->{'movie_results'}[0]{'adult'},
'poster_path' => '/g32WbO9nbY5ydpux5hIoiJkLEQi.jpg',
'original_language' => 'en',
'backdrop_path' => '/bZ4diYf7oyDVaRYeWG42Oify2mB.jpg',
'popularity' => '13.945'
}
]
};
You mention that you thought you'd get JSON output, but got something else. The module made a web request for you, received the JSON response, and translated that to a Perl data structure. That Perl version of the JSON is what you see in the dump.
A JSON object turns into a Perl hash, so that's what you see in the top level of the data structure. That's the single thing find returns (more on that in a moment):
Here's what you have, removing the outer foreach loop:
my #newarray = $narray->{"movie_results"};
foreach my $otherarray (#newarray){
my %innerhash = $otherarray;
print %innerhash;
print "\n";
}
The value in $narray->{"movie_results"} is an array reference. All references are scalars, and those scalars point to some data structure. When you assign that scalar to an array, you just end up with a one element array that's the same reference. Instead, you can
my $movie_results = $narray->{"movie_results"};
You then dereference that reference to treat it as an array:
foreach my $result ( #$movie_results ){ ... }
Or, the v5.24 postfix dereferencing way that I find slightly more pleasing since it reads better, especially when you skip the intermediate variable:
foreach my $result ( $movie_results->#* ){ ... }
foreach my $result ( $narray->{"movie_results"}->#* ){ ... }
That thing in $result is another hash reference.
References and data structures are about half of the content of Intermediate Perl, but there is also the Perl data structures cookbook (perldsc).
Improving your question a bit
You can help us a lot by showing us a complete, working demonstration of your problem. Here's what I cobbled together:
use v5.10;
use TMDB;
use Data::Dumper;
my $tmdb = TMDB->new( apikey => $ENV{TMDB_API_KEY} );
my #results = $tmdb->search->find(
id => 'tt0114694',
source => 'imdb_id'
);
say Dumper( \#results );
There was a question about the results of find. The documentation example shows it returning a list (well, the result being assigned to a named array, which implies that), but there's not actual documentation for find. It returns the decoded JSON from the response. Assigning it to a scalar (which will be a reference) works just fine too:
my $result = $tmdb->search->find(
id => 'tt0114694',
source => 'imdb_id'
);
say Dumper( $results );
The return value comes from TMDB::Sesssion::talk(), which is just this (or the empty list):
return $self->json->decode(
Encode::decode( 'utf-8-strict', $response->{content} ) );
That's not a big deal. That just means you don't need the outer foreach. That's not on you because the example in the docs tells you to do exactly what you did.
Now a better program
Putting all that together, here's a simple program pared down to just what you need:
use v5.10;
use TMDB;
my $tmdb = TMDB->new( apikey => $ENV{TMDB_API_KEY} );
my $result = $tmdb->search->find(
id => 'tt0114694',
source => 'imdb_id'
);
foreach my $item ( $result->{movie_results}->#* ) {
say "Title: $item->{title}";
}
Ref aliasing
There's an experimental feature called ref aliasing that lets you assign a reference to a reference of a named variable. It's an alias, so you can access and change the same data, just with a named variable. Something this is handy when you don't like
use v5.10;
use TMDB;
use experimental qw(refaliasing);
my $tmdb = TMDB->new( apikey => $ENV{TMDB_API_KEY} );
# response is a hash ref, so ref alias to a named hash
\my %result = $tmdb->search->find(
id => 'tt0114694',
source => 'imdb_id'
);
# the part you want is an array ref, so alias that
\my #movie_results = $result{movie_results};
# each item in the array is a hash ref, so alias those too
foreach \my %item ( #movie_results ) {
say "Title: $item{title}";
}
When dealing with reference, use the same syntax as if you weren't, but replace the name of the variable with a block that returns the reference.
%NAME -> %{ $ref } Or just %$ref
$NAME{...} -> ${ $ref }{...} Although $ref->{...} easier to read.
#NAME -> #{ $ref } Or just #$ref
$NAME[...] -> ${ $ref }[...] Although $ref->[...] easier to read.
Let's give $VAR a better name,
my $response = $VAR1;
This means you want
my $results = $response->{movie_results};
for my $result (#$results) {
for my $key (keys(%$result)) {
say "$key: $result->{$key}";
}
}
See
perlreftut
Perl Dereferencing Syntax
%newhash{$newkey} should be $newhash{$newkey}.
i cant resolv this in my mind, its too much to me, perhaps someone can help me:
#Hosts = ("srv1","db1","srv2","db3","srv3","db3","srv3","db4","srv3","db5");
my $count = #Hosts;
$count= $count / 2;
my %hash;
$i = 0;
$ii = 1;
$j = 0;
$jj = 0;
while ($jj < $count) {
$hash{$j}{$Hosts[$i]} = $Hosts[$ii];
$i = $i + 2;
$ii = $ii +2;
$j++;
$jj++
}
print Dumper(\%hash);
Output:
$VAR1 = {
'4' => {
'srv4' => 'db3'
},
'1' => {
'srv2' => 'db3'
},
'3' => {
'srv3' => 'db3'
},
'0' => {
'srv1' => 'db1'
},
'2' => {
'srv3' => 'db3'
}
'5' => {
'srv3' => 'db5'
}
};
I Know this i ugly code, i dont know how to do that better, what i need to do is find douple servers and douple dbs, and put the positions and the string of the duplicates in an array ore something like that, i want to generate a Nagvis Map file out of that.
The Icinga Config file contains am Member string like this:
members srv1, db1, srv2, db3, srv3, db3, srv3, db3, srv4
It has pairs server, db, server, db, here is a sample of the Nagvis Config:
define host {
object_id=5e78fb
host_name=srv1
x=237
y=122
}
define service {
object_id=30646e
host_name=srv1
service_description=db1
x=52
y=122
}
define host {
object_id=021861
host_name=srv2
x=237
y=217
}
define service {
object_id=a5e725
host_name=srv1
service_description=db2
x=52
y=217
}
Thanks in advance
You need to clarify exactly what you want. It's very difficult to tell by your description.
And, your code is in very poor condition. Indenting loops and if statements like this:
while ($jj < $anzahl) {
$hash{$j}{$Hosts[$i]} = $Hosts[$ii];
$i = $i + 2;
$ii = $ii +2;
$j++;
$jj++
}
Makes your code much easier to understand. You also use generic names. What data is stored in #array? Is it a list of systems. Call it #systems. What is $i and $jj suppose to represent? What do you want $hash{$j}{$Hosts[$i]} to represent?
You should always, always, always add the following lines to the top of your program:
use strict;
use warnings;
If you use strict, you must declare all of your variables with my. This makes sure you don't do things like have #array in one place and #Hosts in another. These two lines will catch about 90% of your errors.
I don't know if you want a list of all the DB system that connect to the various servers or if you want a list of the various servers that connect to the DB systems. Therefore, I'll give you both.
I am guessing that your #array is a list of all of your machines and databases in one list:
use strict;
use warnings;
use feature qw(say); # Allows me to use "say" instead of "print"
use Data::Dumper;
my #systems = qw( # The qw(...) is like putting quotes around each word.
svr1 db1 # A nice way to define an array...
srv2 db3
srv3 db3
srv3 db4
srv3 db5
);
my %db_systems; # Database systems with their servers.
my %servers; # Servers with their database systems.
for (;;) { # Loop forever (until I say otherwise)
my $server = shift #systems;
#
# Let's check to make sure that there's a DB machine for this server
#
if ( not #systems ) {
die qq(Cannot get database for server "$server". Odd number of items in array);
}
my $database = shift #systems;
$servers{$server}->{$database} = 1;
$db_systems{$database}->{$server} = 1;
last if not #systems; # break out of loop if there are no more systems
}
say "Servers:" . Dumper \%servers;
say "Databases: " . Dumper \%db_systems;
This produces:
Servers:$VAR1 = {
'srv3' => {
'db4' => 1,
'db3' => 1,
'db5' => 1
},
'svr1' => {
'db1' => 1
},
'srv2' => {
'db3' => 1
}
};
Databases: $VAR1 = {
'db4' => {
'srv3' => 1
},
'db3' => {
'srv3' => 1,
'srv2' => 1
},
'db5' => {
'srv3' => 1
},
'db1' => {
'svr1' => 1
}
};
Is this close to what you want?
Addendum
Hi this is working!! Now i need to understand how to access the Values to print them in my file. This hash of hash thing is kind off ruff to mee. Thanks for that quick Help!
You need to read the Perl tutorial on References and the Perl Reference Page on References.
In Perl, all data is scalar which means that variables talk about single values. In other programming languages, you have structures or records, but not Perl.
Even arrays and hashes are nothing but collections of individual bits of data. What happens when you need something a bit more complex?
A reference is a memory location of another Perl data structure. You could have references to scalar variables like $foo, but that wouldn't do you much good in most circumstances. Where this is helpful is when you have a reference pointing to an array or a hash. This way, you could have much more complex structures that can be used to represent this data.
Imagine an array of ten items ($foo[0] to $foo[9]). Each entry in the array is pointing to another array of ten items. There are now 101 separate arrays being referenced here. We can treat them as a single structure, but it's important to remember that they are separate arrays.
I have a reference to an array at $foo[0]. How do I get access to the array itself? I do what is known as a dereference. To do that, I use curly braces with the right sigil in front. (The sigil is the $, #, or % you see in front of Perl variables:
$foo[0]; # Reference to an array
my #temp = #{ $foo[0] }; # Dereferencing.
my $temp[0]; # Now I can access that inner array
Having to use a temporary array each time I have to dereference it is rather clumsy, so I don't have to:
$foo[0]; # Reference to an array
my $value = ${ $foo[0] }[0]; # Getting the value of an item in my array reference
You can see that last is a bit hard to read. Imagine if I have a hash of a hash of an array of items:
my $phone = ${ ${ ${ $employee{$emp_number} }{phone} }[0] }{NUMBER};
It's a bit unwieldy. Fortunately, Perl allows you a few shortcuts. First, I can nest the references and use the default precedence:
my $phone = $employee{$emp_number}{phone}[0]{NUMBER};
I prefer using the -> notation:
my $phone = $employee{$emp_number}->{phone}->[0]->{NUMBER};
The arrow notion is cleaner because it separates the parts out, and it reminds you these are references!. and, not some complex structure data structure. This helps remind you when you have to do a dereference such as when you use the key, pop, or push commands:
for my $field ( keys %{ $employee } ) { # Dereference the hash
say "Field $field = " . $employee{$emp_number}->{$field}
if ( not ref $employee{$emp_number}->{$field} );
}
Look up the ref to see what it does and why I am only interested in printing out the field if ref returns an empty string.
By now, you should be able to see how to access your hash of hashes using the -> syntax:
my $db_for_server = $servers{$server}->{$database};
And you can use two loops:
for my $server ( keys %servers } {
my %db_systems = %{ $servers{$server} }; # Dereferencing
for my $db_system ( keys %db_systems } {
say "Server $server has a connection to $db_systems{$db_system}";
}
}
Or, without an intermediate hash...
for my $server { keys %servers } {
for my $db_system ( keys %{ $servers{$server} } ) {
say "Server $server has a connection to " . $servers{$server}->{$db_system};
}
}
Now, go out there and get a good book on Modern Perl. You need to learn good programming techniques like using good variable names, indenting, and using strict and warnings in order to help you write better programs that are easier to decipher and support.
I have a hash %AllData pulling data from database using following code:
while(my $Row = $sth1->fetchrow_hashref())
{
if(defined($Row->{LotID}))
{
$AllData->{$Row->{LotID}}->{$Row->{Name}} = $Row->{Details};
}
}
After I'm done with pulling data, I use print Dumper($AllData); to print out All my data which show like:
$VAR1 = {
'4197359' => {
'Short Description' => 'Rock Picker',
'Lot Number' => '1065',
'Description' => 'Rock Picker'
},
'4194148' => {
'Short Description' => 'Large work bench w/ drawers',
'Lot Number' => '1041',
'Description' => 'Large work bench w/ drawers'
},
'4200944' => {
'Lot Number' => '1084',
'Description' => 'Horse Trailer'
}
}
However, when I try to print out the size of the hash or use foreach to access the hash, it shows 0 size and can't access any element within the hash:
print "Hash Size: ", scalar keys %AllData, "\n";
shows:
Hash Size: 0
What's the cause of my problem?
There is no hash %AllData, and if your program didn't raise an error then you haven't got
use strict;
use warnings;
at the head of you program. This is vital for all Perl programs, especially when you are asking others for help with your code.
The hash you're interested in is the one referenced by $AllData, so you need to use this variable and dereference it. Like this
print "Hash Size: ", scalar keys %$AllData, "\n";
Try accessing scalar keys %$AllData in order to access the hash that the reference.. refers to.
$AllData (what you're passing to Dumper() ) is a reference to a hash (a 'hashref')
%AllData is a different thing to Perl than $AllData. If this hasn't been set yet and perl isn't complaining, you may need to try putting use strict; at the top of your script so that you can be warned of these (and other) types of errors.
Maybe you need to dereference the hash first:
print "Hash Size: ", scalar keys %{ $AllData }, "\n";
should represent a hash ref as $%hash instead of %hash to print
I am attempting to port some code from PHP that basically boils down to property overloading. That is, if you try to get or set a class property that is not actually defined as a part of the class, it will send that information to a function that will pretty much do anything I want with it. (In this case, I want to search an associative array within the class before giving up.)
However, Perl is... quite a bit different from PHP, given that classes are already hashes. Is there any way that I can apply some equivalent of __get() and __set() to a Perl "class" that will remain completely encapsulated in that package, transparent to anything trying to actually get or set properties?
EDIT: The best way to explain this may be to show you code, show the output, and then show what I want it to output.
package AccessTest;
my $test = new Sammich; #"improper" style, don't care, not part of the question.
say 'bacon is: ' . $test->{'bacon'};
say 'cheese is: ' . $test->{'cheese'};
for (keys $test->{'moreProperties'}) {
say "$_ => " . $test->{'moreProperties'}{$_};
}
say 'invalid is: ' . $test->{'invalid'};
say 'Setting invalid.';
$test->{'invalid'} = 'true';
say 'invalid is now: ' . $test->{'invalid'};
for (keys $test->{'moreProperties'}) {
say "$_ => " . $test->{'moreProperties'}{$_};
}
package Sammich;
sub new
{
my $className = shift;
my $this = {
'bacon' => 'yes',
'moreProperties' => {
'cheese' => 'maybe',
'ham' => 'no'
}
};
return bless($this, $className);
}
This currently outputs:
bacon is: yes
Use of uninitialized value in concatenation (.) or string at ./AccessTest.pl line 11.
cheese is:
cheese => maybe
ham => no
Use of uninitialized value in concatenation (.) or string at ./AccessTest.pl line 17.
invalid is:
Setting invalid.
invalid is now: true
cheese => maybe
ham => no
Now, I need to make modifications to Sammich only, without making any changes at all to the initial AccessTest package, that will result in this:
bacon is: yes
cheese is: maybe
cheese => maybe
ham => no
invalid is: 0
Setting invalid.
invalid is now: true
cheese => maybe
ham => no
invalid => true
As you can see, the desired effect is that the 'cheese' property, since it's not a part of the test object directly, would instead be grabbed from the 'moreProperties' hash. 'invalid' would attempt the same thing, but since it is neither a direct property nor in 'moreProperties', it would act in whatever way programmed - in this case, I would want it to simply return the value 0, without any errors or warnings. Upon attempting to set the 'invalid' property, it would not be added to the object directly, because it's not already there, but would instead be added to the 'moreProperties' hash.
I'm expecting this to take more than the six or so lines it would require in PHP, but as it is a very important concept of OOP, I fully expect Perl to handle it somehow.
As I have said in my comments, the reason this problem is hard is that you aren't following one of the golden rules of Object-Oriented Programming, namely encapsulation. How can you expect to intercept a call that isn't a method? If your exposed API consists of getter/setters then you can intercept an unknown method call with an AUTOLOAD method. If you don't you may use #pilcrow's noble suggestion of using a tied hash (Edit: or #tchrist's valiant use of the overload pragma); still this is more a tribute to Perl's flexibility than your API.
To do this more correctly (and yes I see you "require" that the API not be modified, if you choose to ignore this, then call this post a message to future readers).
#!/usr/bin/env perl
use v5.10; # say
use strict;
use warnings;
use MooseX::Declare;
use Method::Signatures::Modifiers;
class Sammich {
has 'bacon' => ( isa => 'Str', is => 'rw', default => 'yes' );
has 'more' => (
isa => 'HashRef',
is => 'rw',
default => sub{ {
cheese => 'maybe',
ham => 'no',
} },
);
our $AUTOLOAD;
method AUTOLOAD ($val?) {
# get unknown method name
my $attr = (split( /::/, $AUTOLOAD))[-1];
my $more = $self->more;
# see if that method name exists in "more"
if (exists $more->{$attr}) {
# if so, are there new values? then set
if (defined $val) {
$more->{$attr} = $val;
}
# ... and return
return $more->{$attr};
}
# attr does not exist, so set it or initialize it to 0
$more->{$attr} = defined $val ? $val : 0;
return $more->{$attr};
}
}
# I don't care that you don't care
my $test = Sammich->new();
say 'bacon is: ' . $test->bacon;
say 'cheese is: ' . $test->cheese;
for (keys %{ $test->more }) {
say "$_ => " . $test->more->{$_};
}
say 'invalid is: ' . $test->invalid;
say 'Setting invalid.';
$test->invalid( 'true' );
say 'invalid is now: ' . $test->invalid;
for (keys %{ $test->more }) {
say "$_ => " . $test->more->{$_};
}
Some may say that my wording here is harsh and perhaps it is. I try to help those who will be helped, therefore seeing a bolded message like
I need to make modifications to Sammich only, without making any changes at all to the initial AccessTest package
then demanding that Perl bow to your whim
I'm expecting this to take more than the six or so lines it would require in PHP, but as it is a very important concept of OOP, I fully expect Perl to handle it somehow.
is irksome. I hope that future readers will see this as a case example of why encapsulation helps in the long run.
Update to the update
(I am receiving anonymous downvotes, presumably for abetting your misguided approach. :) )
Just to be clear, the question you pose is an "XY Problem", specifically an artifact of the mistaken translation of OO technique from PHP to Perl. For example, as mentioned passim in this question, object properties in perl generally should not be implemented as directly accessed hash(ref) elements. That's the wrong "X".
Jumping from one language to another introduces more than merely syntactical differences.
Update
You can accomplish what you want with something like this:
package UnfortunateHack; {
use Tie::Hash;
our #ISA = qw(Tie::StdHash);
sub FETCH {
my ($self, $key) = #_;
return exists $self->{$key}
? $self->{$key}
: $self->{moreProperties}->{$key};
}
}
...
package Sammich;
sub new {
my $class = shift;
tie my %this, 'UnfortunateHack';
%this = ( bacon => 'yes',
moreProperties => { ... } );
bless \%this, $class;
}
Original Answer
If I understand your intent — to intercept $obj->property calls where TheClass::property isn't necessarily defined — then perl's AUTOLOAD in an OO context will do what you want.
From the linked docs (perlobj):
If you call a method that doesn't exist in a class, Perl will throw an error. However, if that class or any of its parent classes defines an AUTOLOAD method, that AUTOLOAD method is called instead.
You are completely violating encapsulation. To prove it to you, comment out the bless from &Sammich::new so that it returns a plain hash reference.
package Sammich;
sub new {
my $className = shift;
my $this = {
'bacon' => 'yes',
'moreProperties' => {
'cheese' => 'maybe',
'ham' => 'no'
}
};
# don't even bother blessing it
# return bless($this, $className);
}
The only way to get what you want it is to use magic.
In Perl classes are more than hashes, they are built on packages and you can define there whatever method you want and it remains encapsulated in that package/class.
You can see a code example in the Object-Oriented Programming in Perl Tutorial.
In perl I wanted to debug some module code, so I temporarily added the following line to such source code:
print $${${$${$$h[1]{$j}}{proxy_cache}}{$e}}{'fetch_handler'}{'ownerDocument'}
...and it prints:
CODE(0x9b2b3e0)
What "CODE" means? I expected HASH(0x???????). I am pretty new in Perl, so please explain me this, as goooooogling for +Perl +CODE is not helpful :)
I was looking for url of ownerDocument information, btw.
[UPDATE]
I am trying to use module WWW::Scripter for my needs and I already found several bugs that author of this module (Father Chrysostomos) already fixed based on my inputs.
Now I'm "debugging" some issues with images that are created dynamically in JavaScript (for example ((new Image()).src='http://...'), as those images are now not included in $w->images results.
If you take a look at sub update_html in module source [http://cpansearch.perl.org/src/SPROUT/WWW-Scripter-0.026/lib/WWW/Scripter.pm], there is a line that starts with
$h && $h->eval($self, $code ...
This is a section that I need to debug. I am trying to "search" for new images in DOM after script is evaluated. I was able to find image elements pretty easy, but now I am trying to find information to which document they belong to, as I need to get them with correct referer information. Some images are created within frames, iframes, scripts, etc. If incorrect referer information is used, then it may lead to incorrect response, as most of such (new Image()).src='http://...' images are used for tracking with cookies, not for real image content. To get correct content of document, all these special images need to be properly processed and without correct referer it does not work...
It's a code reference, e.g.:
my $var = sub { ... };
print "$var\n";
This is a bit too long for a comment, but it's not a direct answer to your question.
I wanted to figure out your data structure, which I fully realize you might not control. I'm curious why you have to deal with that, and if you have any hair, or sanity, left.
The multiple references are a bit painful, but it also reminds me of stupid things I used to do with references and that I even presented at the first Perl Conference.
When I first started using references, I thought, stupidly, that every time that I wanted to pass a reference I had to take a reference, even if the thing was already a reference. I'd end up with something ugly like $$$$ref:
my $string = 'Buster';
some_sub( \$string );
sub some_sub {
my $ref = shift;
some_other_sub( \$ref );
}
sub some_other_sub {
my $ref = shift;
yet_another_sub( \$ref );
}
sub yet_another_sub {
my $ref = shift;
print "$$$$ref\n"; #fuuuuugly!
}
This gets even worse when you start taking to references to aggregates, which is what I think is happening in your data structure. Since a reference to a reference is just a scalar, as is the original reference, you can't dereference it by lining up subscripts. Hence, all of the $${ } in your line.
I couldn't see what was happening until I started from the inside out, and even then I just used trial and error until I got things that worked.
The first level is an array reference that contains a hash reference at index 1. That's not so hard or ugly:
my $j = 'foo';
my $e = 'baz';
my $h = [];
$h->[1] = { foo => 'bar' }; # to get to $$h[1]{$j}
print "1: $h->[1]{$j}\n";
The next level is a bit weird. To get $${ ... }{proxy_cache}, you need a reference to a hash reference:
$h->[1] = {
foo => \ { proxy_cache => 'duck' } # ref to hash reference
};
print "2. $${ $$h[1]{$j} }{proxy_cache}\n";
I'm not sure how you are building this data structure, but you should look for places where you already have a hash reference and not take another ref. That's the stupid thing I was doing in my youth. It might look like this:
sub some_sub {
my $hash = shift;
$h->[1] = {
foo => \ $hash # don't do that!
};
The next part isn't that bad. It's just a regular hash reference as the value (instead of duck):
$h->[1] = {
foo => \ { proxy_cache => { $e => 'quux' } }
};
print "3. ${ $${ $$h[1]{$j} }{proxy_cache} }{$e}\n";
The next level is another reference to a hash reference:
$h->[1] = {
foo => \ {
proxy_cache => {
$e => \ { fetch_handler => 'zap' }
}
}
};
print "4. $${ ${ $${ $$h[1]{$j} }{proxy_cache} }{$e} }{'fetch_handler'}\n";
Finally, I get to the last key, ownerDocument, and assign a subroutine reference:
$h->[1] = {
foo => \ {
proxy_cache => {
$e => \ { fetch_handler => {
ownerDocument => sub { print "Buster\n" },
}
}
}
}
};
print "5. $${ ${ $${ $$h[1]{$j} }{proxy_cache} }{$e} }{'fetch_handler'}{'ownerDocument'}\n";
The output is the CODE(0x.......) that you've already seen.
I wanted to simplify that, but there's not much to remove because of those pesky non-aggregate references. This removes only three non-whitespace of characters to line up the {$e} key:
print "6. ";
print $${ $${ $h->[1]{$j} }{proxy_cache}{$e} }{'fetch_handler'}{'ownerDocument'};
print "\n";