0

I have a Perl hash reference $artifact that I am printing using Data::Dumper.

The output of print Dumper ($artifact); returns the following.

$VAR1 = bless( {
             '_content' => '{
  "results" : [ {
"uri" : "http://localhost:port/myfile.tar"
   } ]
}',);

However, I am having trouble trying to specifically access the value of URI, i.e. I want to check if uri has a value or not.

Thanks!

Edit: Just some context. I am using the 'artifact_search' method from the Artifactory::Client perl module

Sobrique
  • 52,974
  • 7
  • 60
  • 101
user2402135
  • 371
  • 1
  • 6
  • 19
  • 4
    Is this a direct copy/paste of the Dumper output? This doesn't parse (you're missing a closing `}` on the `bless`) and it's unlikely that you have a blessed reference without it being blessed into a particular class (which would presumably include methods for accessing the contents). – Michael Carman Feb 02 '15 at 13:59
  • @MichaelCarman Apologies - I just extracted the part of the Data Dumper output that I needed to work with. print Dumper ($artifact) returns a lot more but it was not relevant. – user2402135 Feb 02 '15 at 14:17
  • 3
    From the [synopsis section of the `Artifactory::Client` documentation](https://metacpan.org/pod/Artifactory::Client#SYNOPSIS): "Every public method provided in this module returns a `HTTP::Response` object." You should be using the [`HTTP::Response`](https://metacpan.org/pod/HTTP::Response) API instead of poking around in the internals yourself. – ThisSuitIsBlackNot Feb 02 '15 at 15:18

3 Answers3

6

To extract information from a hash reference, first you need to dereference. You can either:

print ${$artifact}{uri},"\n"; 

In this specific case, you can omit the braces and just do:

print $$artifact{uri},"\n"; 

But be aware that that can be ambiguous so the style of notation doesn't always work for dereferencing.

Or the newer, and probably clearer notation (e.g. like object oriented)

print $artifact->{uri},"\n";

However, there is a BIG alarm bell here - bless - this means you're manipulating an object, probably. Poking inside an object is VERY dirty. You shouldn't ever do it. Usually the object will contain an accessor method to give you the information you need. By convention, an _ prefix denotes private e.g. 'don't mess with this'. (Not that you should anyway)

As noted in the comments - this is a JSON text string embedded within your object. So if you were really set on doing this - you can parse the JSON, turn it into a perl data structure, then use that.

But far more likely - the object you're manipulating has some accessor methods built in, and you should use them.

So given your example above:

#!/usr/bin/perl

use strict;
use warnings;
use Data::Dumper;
use JSON;

my $hashref = {
    _content => '{
      "results" : [ {
"uri" : "http://localhost:port/myfile.tar"
   } ]
}'
};

print Dumper \$hashref;

my $json    = JSON->new();
my $json_ob = $json->decode( $hashref->{_content} );
print Dumper \$json_ob;
print $json_ob ->{results}->[0]->{uri};

However as mentioned in comments, you're using: Artifactory::Client which quite sensibly uses LWP.

Every public method provided in this module returns a HTTP::Response object.

Referring the HTTP::Response docs gives this sample:

if ($artifact->is_success) {
    print $artifact->decoded_content;
}
else {
    print STDERR $artifact->status_line, "\n";
}
HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
Sobrique
  • 52,974
  • 7
  • 60
  • 101
2

As far as Perl is concerned, your $artifact variable is a hash reference with a single key/value pair. The URI is embedded in the value, which appears to be JSON. You need to extract the value, parse it, and then fetch the URI.

JSON is a valid subset of YAML, so you can use a YAML module to parse it.

use YAML::XS;
my $content = $artifact->{_content};
my $yaml    = Load($content);
print $yaml->{results}[0]{uri}; # http://localhost:port/myfile.tar

That said, a leading underscore on a method/attribute name usually mean that they're "private" to the class and shouldn't be used externally. Peeking behind the API (which we can't know without knowing where the data came from and what class $artifact is blessed into) is fragile.

Michael Carman
  • 30,628
  • 10
  • 74
  • 122
  • 1
    The Rails people thought they could use a YAML parser to handle JSON too :) If you want to parse JSON, why make it more compilcated? Use a JSON parser that doesn't have all the extra features, such as object creation and destruction. – brian d foy Feb 02 '15 at 20:00
0

That's not the exact output you get from Dumper, is it? If the Dumper output includes a call to bless then it should also include the name of the class that the reference has been blessed into.

The naive approach would be this:

print $artifact->{_content}{results}[0]{uri};

But given that what you seem to have there is an object (a blessed hash reference) then there will almost certainly be methods on the class to access the attributes of the object. You should use those rather than digging around in the internals of the object.

Update: Yes, I missed that the whole of the _content key was a JSON string. Sorry about that :-/

Dave Cross
  • 68,119
  • 3
  • 51
  • 97
  • 1
    While I agree that there should be a class it is possible to bless a reference without specifying one. You can't access `uri` directly, though: everything after `_content` is a JSON/YAML string. – Michael Carman Feb 02 '15 at 14:07
  • Indeed - I just got caught out - that's all a string, not a data structure despite looking like `Data::Dumper` output at first glance. – Sobrique Feb 02 '15 at 14:17
  • 1
    @MichaelCarman: You can certainly call `bless()` without specifying a class. But your blessed hash will still have a class attached - it will be the current package. Try `perl -MData::Dumper -E' $o=bless({}), say Dumper $o'`. – Dave Cross Feb 02 '15 at 17:02