I have a perl hash that is obtained from parsing JSON. The JSON could be anything a user defined API could generated. The goal is to obtain a date/time string and determine if that date/time is out of bounds according to a user defined threshold. The only issue I have is that perl seems a bit cumbersome when dealing with hash key/subkey iteration. How can I look through all the keys and determine if a key or subkey exists throughout the hash? I have read many threads throughout stackoverflow, but nothing that exactly meets my needs. I only started perl last week so I may be missing something... Let me know if that's the case.
Below is the "relevant" code/subs. For all code see: https://gitlab.com/Jedimaster0/check_http_freshness
use warnings;
use strict;
use LWP::UserAgent;
use Getopt::Std;
use JSON::Parse 'parse_json';
use JSON::Parse 'assert_valid_json';
use DateTime;
use DateTime::Format::Strptime;
# Verify the content-type of the response is JSON
eval {
        assert_valid_json ($response->content);
};
if ( $@ ){
        print "[ERROR] Response isn't valid JSON. Please verify source data. \n$@";
        exit EXIT_UNKNOWN;
} else {
        # Convert the JSON data into a perl hashrefs
        $jsonDecoded = parse_json($response->content);
        if ($verbose){print "[SUCCESS] JSON FOUND -> ", $response->content , "\n";}
        if (defined $jsonDecoded->{$opts{K}}){
                if ($verbose){print "[SUCCESS] JSON KEY FOUND -> ", $opts{K}, ": ", $jsonDecoded->{$opts{K}}, "\n";}
                NAGIOS_STATUS(DATETIME_DIFFERENCE(DATETIME_LOOKUP($opts{F}, $jsonDecoded->{$opts{K}})));
        } else {
                print "[ERROR] Retreived JSON does not contain any data for the specified key: $opts{K}\n";
                exit EXIT_UNKNOWN;
        }
}
sub DATETIME_LOOKUP {
        my $dateFormat = $_[0];
        my $dateFromJSON = $_[1];
        my $strp = DateTime::Format::Strptime->new(
                pattern   => $dateFormat,
                time_zone => $opts{z},
                on_error  => sub { print "[ERROR] INVALID TIME FORMAT: $dateFormat OR TIME ZONE: $opts{z} \n$_[1] \n" ; HELP_MESSAGE(); exit EXIT_UNKNOWN; },
        );
        my $dt = $strp->parse_datetime($dateFromJSON);
        if (defined $dt){
                if ($verbose){print "[SUCCESS] Time formatted using -> $dateFormat\n", "[SUCCESS] JSON date converted -> $dt $opts{z}\n";}
                return $dt;
        } else {
                print "[ERROR] DATE VARIABLE IS NOT DEFINED. Pattern or timezone incorrect."; exit EXIT_UNKNOWN
        }
}
# Subtract JSON date/time from now and return delta
sub DATETIME_DIFFERENCE {
        my $dateInitial = $_[0];
        my $deltaDate;
        # Convert to UTC for standardization of computations and it's just easier to read when everything matches.
        $dateInitial->set_time_zone('UTC');
        $deltaDate = $dateNowUTC->delta_ms($dateInitial);
        if ($verbose){print "[SUCCESS] (NOW) $dateNowUTC UTC - (JSON DATE) $dateInitial ", $dateInitial->time_zone->short_name_for_datetime($dateInitial), " = ", $deltaDate->in_units($opts{u}), " $opts{u} \n";}
        return $deltaDate->in_units($opts{u});
}
Sample Data
{
  "localDate":"Wednesday 23rd November 2016 11:03:37 PM",
  "utcDate":"Wednesday 23rd November 2016 11:03:37 PM",
  "format":"l jS F Y h:i:s A",
  "returnType":"json",
  "timestamp":1479942217,
  "timezone":"UTC",
  "daylightSavingTime":false,
  "url":"http:\/\/www.convert-unix-time.com?t=1479942217",
  "subkey":{
    "altTimestamp":1479942217,
    "altSubkey":{
      "thirdTimestamp":1479942217
    }
  }
}
[SOLVED]
I have used the answer that @HåkonHægland provided. Here are the below code changes. Using the flatten module, I can use any input string that matches the JSON keys. I still have some work to do, but you can see the issue is resolved. Thanks @HåkonHægland.
use warnings;
use strict;
use Data::Dumper;
use LWP::UserAgent;
use Getopt::Std;
use JSON::Parse 'parse_json';
use JSON::Parse 'assert_valid_json';
use Hash::Flatten qw(:all);
use DateTime;
use DateTime::Format::Strptime;
# Verify the content-type of the response is JSON
eval {
        assert_valid_json ($response->content);
};
if ( $@ ){
        print "[ERROR] Response isn't valid JSON. Please verify source data. \n$@";
        exit EXIT_UNKNOWN;
} else {
        # Convert the JSON data into a perl hashrefs
        my $jsonDecoded = parse_json($response->content);
        my $flatHash = flatten($jsonDecoded);
        if ($verbose){print "[SUCCESS] JSON FOUND -> ", Dumper($flatHash), "\n";}
        if (defined $flatHash->{$opts{K}}){
                if ($verbose){print "[SUCCESS] JSON KEY FOUND -> ", $opts{K}, ": ", $flatHash>{$opts{K}}, "\n";}
                NAGIOS_STATUS(DATETIME_DIFFERENCE(DATETIME_LOOKUP($opts{F}, $flatHash->{$opts{K}})));
        } else {
                print "[ERROR] Retreived JSON does not contain any data for the specified key: $opts{K}\n";
                exit EXIT_UNKNOWN;
        }
}
Example:
./check_http_freshness.pl -U http://bastion.mimir-tech.org/json.html -K result.creation_date -v
[SUCCESS] JSON FOUND -> $VAR1 = {
          'timestamp' => '20161122T200649',
          'result.data_version' => 'data_20161122T200649_data_news_topics',
          'result.source_version' => 'kg_release_20160509_r33',
          'result.seed_version' => 'seed_20161016',
          'success' => 1,
          'result.creation_date' => '20161122T200649',
          'result.data_id' => 'data_news_topics',
          'result.data_tgz_name' => 'data_news_topics_20161122T200649.tgz',
          'result.source_data_version' => 'seed_vtv: data_20161016T102932_seed_vtv',
          'result.data_digest' => '6b5bf1c2202d6f3983d62c275f689d51'
        };
Odd number of elements in anonymous hash at ./check_http_freshness.pl line 78, <DATA> line 1.
[SUCCESS] JSON KEY FOUND -> result.creation_date:
[SUCCESS] Time formatted using -> %Y%m%dT%H%M%S
[SUCCESS] JSON date converted -> 2016-11-22T20:06:49 UTC
[SUCCESS] (NOW) 2016-11-26T19:02:15 UTC - (JSON DATE) 2016-11-22T20:06:49 UTC = 94 hours
[CRITICAL] Delta hours (94) is >= (24) hours. Data is stale.
 
     
    