我有一个从解析JSON获得的perl哈希。 JSON可以是用户定义的API可以生成的任何内容。目标是获取日期/时间字符串,并根据用户定义的阈值确定该日期/时间是否超出范围。我唯一的问题是perl在处理散列键/子键迭代时似乎有点麻烦。如何查看所有键并确定整个哈希中是否存在键或子键?我已经在stackoverflow中读了很多线程,但没有任何东西完全符合我的需求。我上周才开始perl,所以我可能会遗漏一些东西......如果是这样,请告诉我。
以下是“相关”代码/ subs。对于所有代码,请参阅: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});
}
样本数据
{
"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
}
}
}
[解决]
我使用了@HåkonHægland提供的答案。以下是代码更改。使用flatten模块,我可以使用任何与JSON键匹配的输入字符串。我还有一些工作要做,但你可以看到问题已经解决了。谢谢@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;
}
}
示例:
./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.
答案 0 :(得分:3)
您可以尝试使用Hash::Flatten
。例如:
use Hash::Flatten qw(flatten);
my $json_decoded = parse_json($json_str);
my $flat = flatten( $json_decoded );
say "found" if grep /(?:^|\.)\Q$key\E(?:\.?|$)/, keys %$flat;
答案 1 :(得分:2)
您可以使用Data::Visitor::Callback遍历数据结构。它允许您为结构中的不同类型的数据类型定义回调。由于我们只是查看哈希,因此相对简单。
以下程序有一个预定义的键列表(在您的情况下将是用户输入)。我将您的示例JSON转换为Perl hashref并将其包含在代码中,因为转换不相关。该程序访问此数据结构中的每个hashref(包括顶级)并运行回调。
Perl中的回调是代码引用。这些可以通过两种方式创建。我们正在进行匿名子程序(有时在其他语言中称为lambda function)。回调传递两个参数:访问者对象和当前数据子结构。
我们将迭代我们想要查找的所有密钥,并检查它们是否在当前数据结构中exist
。如果我们看到一个,我们会计算它在%seen
哈希中的存在。使用哈希来存储我们看到的东西是Perl中常见的习语。
我们在这里使用 postfix if
,方便易读。 %seen
是哈希,因此我们使用$key
访问$seen{$key}
后面的值,而$data
是哈希引用,因此我们使用解除引用运算符->
来$key
访问$data->{$key}
后面的值。
回调需要我们再次返回$data
,以便继续。最后一行就在那里,它并不重要。
我已使用Data::Printer输出%seen
哈希值,因为它很方便。如果需要,您还可以使用Data::Dumper。在生产中,你不需要那样。
use strict;
use warnings;
use Data::Printer;
use Data::Visitor::Callback;
my $from_json = {
"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" =>
0, # this was false, I used 0 because that's a non-true value
"url" => "http:\/\/www.convert-unix-time.com?t=1479942217",
"subkey" => {
"altTimestamp" => 1479942217,
"altSubkey" => {
"thirdTimestamp" => 1479942217
}
}
};
my @keys_to_find = qw(timestamp altTimestamp thirdTimestamp missingTimestamp);
my %seen;
my $visitor = Data::Visitor::Callback->new(
hash => sub {
my ( $visitor, $data ) = @_;
foreach my $key (@keys_to_find) {
$seen{$key}++ if exists $data->{$key};
}
return $data;
},
);
$visitor->visit($from_json);
p %seen;
该程序输出以下内容。请注意,这是不 Perl数据结构。 Data :: Printer不是一个序列化程序,它是一种使数据易于读取的工具。
{
altTimestamp 1,
thirdTimestamp 1,
timestamp 1
}
既然你也想限制输入,这里有一个例子。以下程序是对上述程序的修改。它允许为每个必需的密钥提供一组不同的约束。
我已经使用a dispatch table完成了这项工作。从本质上讲,这是一个包含代码引用的哈希。有点像我们为访客使用的回调。
我所包含的限制是用日期做一些事情。在Perl中使用日期的简单方法是核心模块Time::Piece。这里有很多关于各种日期事物的问题,其中Time :: Piece就是答案。
我每个键只做了一个约束,但你可以轻松地在这些代码引用中包含几个检查,或者创建一个代码引用列表并将它们放在数组引用(keys => [ sub(), sub(), sub() ]
)中然后稍后重复。
在访问者回调中,我们现在还跟踪%passed
约束检查的密钥。我们使用$coderef->($arg)
调用coderef。如果约束检查返回a true value,则会在哈希中记录它。
use strict;
use warnings;
use Data::Printer;
use Data::Visitor::Callback;
use Time::Piece;
use Time::Seconds; # for ONE_DAY
my $from_json = { ... }; # same as above
# prepare one of the constraints
# where I'm from, Christmas eve is considered Christmas
my $christmas = Time::Piece->strptime('24 Dec 2016', '%d %b %Y');
# set up the constraints per required key
my %constraints = (
timestamp => sub {
my ($epoch) = @_;
# not older than one day
return $epoch < time && $epoch > time - ONE_DAY;
},
altTimestamp => sub {
my ($epoch) = @_;
# epoch value should be an even number
return ! $epoch % 2;
},
thirdTimestamp => sub {
my ($epoch) = @_;
# before Christmas 2016
return $epoch < $christmas;
},
);
my %seen;
my %passed;
my $visitor = Data::Visitor::Callback->new(
hash => sub {
my ( $visitor, $data ) = @_;
foreach my $key (%constraints) {
if ( exists $data->{$key} ) {
$seen{$key}++;
$passed{$key}++ if $constraints{$key}->( $data->{$key} );
}
}
return $data;
},
);
$visitor->visit($from_json);
p %passed;
这次的输出是:
{
thirdTimestamp 1,
timestamp 1
}
如果您想了解有关调度表的更多信息,请查看Mark Jason Dominus撰写的 Higher Order Perl 一书的第二章,该书合法免费提供here。< / p>