bigdatageek has asked for the wisdom of the Perl Monks concerning the following question:

!!!! UPDATE: Solved by stevieb !!!! Hello! A little background: I am developing a solution for my organization to automate Data Integration from Salesforce to Teradata. I wrote the whole thing already, mainly in bash, and I picked up Perl along the way. Now I am trying to recreate the process purely in Perl. I came across the WWW::Salesforce module which seems powerful but there really isn't much information out there. This is what I'm trying to do: I run an API called describeGlobal(). It returns a HUGE amount of data, most of which I don't need. I have been able to narrow down the result to the data that is relevant to me using ->result->{sobjects}. What I end up with is ALMOST an Array of Hashes, except for one difference (*I think): There are brackets instead of parentheses.
#!/usr/bin/perl use warnings; use strict; use diagnostics; use WWW::Salesforce; use Data::Dumper; my $sforce = eval { WWW::Salesforce->login( username => 'xxxxxxx', password => 'xxxxxxx', serverurl=> 'https://xxxxxxx.salesforce.com/servic +es/Soap/u/34.0'); }; die "Could not login to SFDC: $@" if $@; my @tableattribute_ref=$sforce->describeGlobal()->result->{sobjects};
Here is a sample of a Dumper output. See what I mean? Does it matter that there are brackets here instead of parentheses?
[ { 'custom' => 'true', 'deletable' => 'false', 'retrieveable' => 'true', 'layoutable' => 'true', 'queryable' => 'true', 'createable' => 'false', 'customSetting' => 'false', 'deprecatedAndHidden' => 'false', 'undeletable' => 'false', 'triggerable' => 'true', 'keyPrefix' => 'a0k', 'name' => 'x1', 'updateable' => 'false', 'feedEnabled' => 'false', 'mergeable' => 'false', 'searchable' => 'true', 'replicateable' => 'true', 'labelPlural' => 'x1s', 'activateable' => 'false', 'label' => 'x1' }, { 'custom' => 'true', 'deletable' => 'false', 'retrieveable' => 'true', 'layoutable' => 'true', 'queryable' => 'true', 'createable' => 'false', 'customSetting' => 'true', 'deprecatedAndHidden' => 'false', 'undeletable' => 'false', 'triggerable' => 'false', 'keyPrefix' => 'a16', 'name' => 'x2', 'updateable' => 'false', 'feedEnabled' => 'false', 'mergeable' => 'false', 'searchable' => 'true', 'replicateable' => 'true', 'labelPlural' => 'x2s', 'activateable' => 'false', 'label' => 'x2' }, { 'custom' => 'false', 'deletable' => 'true', 'retrieveable' => 'true', 'layoutable' => 'false', 'queryable' => 'true', 'createable' => 'true', 'customSetting' => 'false', 'deprecatedAndHidden' => 'false', 'undeletable' => 'false', 'triggerable' => 'false', 'keyPrefix' => '083', 'name' => 'x3', 'updateable' => 'false', 'feedEnabled' => 'false', 'mergeable' => 'false', 'searchable' => 'false', 'replicateable' => 'true', 'labelPlural' => 'x3s', 'activateable' => 'false', 'label' => 'x3' } ];
I need to pull the names out of this to create an array. Here's an alternative, I can pull the names just fine 1 by 1.
my $tableattribute_ref=$sforce->describeGlobal()->result->{sobjects}[# +]{name};
Then I can add it to a loop... I haven't added the piece that will kill the loop yet, just testing it out.
my $count; $count = 0; while ($count >= 0) { $tableattribute_ref=$sforce->describeGlobal()->result->{sobjects}[$cou +nt]{name}; #This is where I can push names to an array $count++; }
Problem is this solution takes forever... (like 10 minutes). I already have an alternative where I bypass WWW::Salesforce completely and just use WWW::Curl::Share. It runs super fast but there's a lot more lines of code because I'm continually creating and submitting XML forms via Curl. I'd much rather use a packaged approach like WWW::Salesforce. Any advice would be much appreciated! Thank you so much for your time. -bigdatageek

Replies are listed 'Best First'.
Re: Salesforce Data Parser
by stevieb (Canon) on Aug 06, 2015 at 16:08 UTC

    Welcome to the Monastery!

    I think you'd be better off not making an API call each time through while. This may help the speed issue. What it does is it gathers all the data up front, then loops over the saved array reference, and for each element (which are hash references), extracts the name value and populates your array with it. It's UNTESTED.

    $tableattribute_ref = $sforce->describeGlobal()->result->{sobjects}; my @names; for my $href (@{ $tableattribute_ref }) { push @names, $href->{name}; }

    Edit: Regarding the brackets/parens, the outer part of your structure is an array reference which always uses [], and the inner elements are hash references, which are always denoted by {}. You won't see parens in such a data structure.

    -stevieb

      Thank you so much!! Haha, I tried so many variations of for, foreach, hash keys, etc. and just could not get it. That worked and it does exactly what I want. Again, thanks so much!

        You're very welcome, I'm glad it worked.

        A little more info on when you said "is ALMOST an Array of Hashes". When printing with Data::Dumper, you always pass in a reference to the structure you're printing, so if the outermost part of the structure is an array (), it'll automatically get converted to [] in the output as you've passed in a reference to the array. In fact, the API itself returns the array as a reference in the first place, so you wouldn't need to take a reference to it when passing it into Dumper. These are equivalent when referring to Data::Dumper...

        my @array = (1, 2, 3); print Dumper \@array;
        my $aref = [1, 2, 3]; print Dumper $aref;

        So it is really an Array of Hashes (AoH), it just looks a tiny bit different when the outermost part of the structure is a reference. You also need to access and use them slightly differently as well (by using dereference operators).

Re: Salesforce Data Parser
by 1nickt (Canon) on Aug 06, 2015 at 17:47 UTC

    Hi bigdatageek, I just wondered if you had seen RFC: App::SFDC.

    Update: fixed link
    The way forward always starts with a minimal test.

      Hey 1nickt, thanks for the shoutout!

      bigdatageek: TIMTOWTDI

      use strict; use warnings; use WWW::SFDC; use Data::Dumper; my @names = map {m'objects/(.*).object'} WWW::SFDC->new( username => ..., password => ..., url => 'https://login.salesforce.com' )->Metadata->listMetadata({type => 'CustomObject'}); print Dumper \@names;
      I did not come across that, interesting.. thanks for the link always good to know of multiple options!

        ali0sha is around here all the time and actively working on that code I believe. Maybe you all could have a meeting of the minds.

        The way forward always starts with a minimal test.